From de1f4f0d86f360e19c77ae17198569e7eca09a25 Mon Sep 17 00:00:00 2001 From: stefan schippers Date: Thu, 8 Jun 2023 10:40:52 +0200 Subject: [PATCH] added commands `xschem switch [win_path|schname]`, `xschem destroy_all [force]`, added `[force]` option to xschem exit --- doc/xschem_man/developer_info.html | 17 +++-- src/callback.c | 7 +- src/scheduler.c | 61 ++++++++++++---- src/xinit.c | 107 +++++++++++++++++++---------- src/xschem.tcl | 14 ++-- 5 files changed, 139 insertions(+), 67 deletions(-) diff --git a/doc/xschem_man/developer_info.html b/doc/xschem_man/developer_info.html index 2565f969..ba9dfddc 100644 --- a/doc/xschem_man/developer_info.html +++ b/doc/xschem_man/developer_info.html @@ -490,6 +490,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns" +
  • abort_operation
  •     Resets UI state, unselect all and abort any pending operation 
  • add_symbol_pin
  • @@ -566,6 +567,10 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        instance number to descend into for vector instances (default: 0). 
  • descend_symbol
  •     Descend into the symbol view of selected component instance 
    +
  • destroy_all [force]
  • +   Close all additional windows/.tabs. If 'force' is given do not ask for
    +   confirmation for changed schematics
    +   Returns the remaining # of windows/tabs in addition to main window/tab 
  • display_hilights [nets|instances]
  •     Print a list of highlighted objects (nets, net labels/pins, instances)
        if 'instances' is specified list only instance highlights
    @@ -595,7 +600,9 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        
  • exit [closewindow]
  •     Exit the program, ask for confirm if current file modified.
        if 'closewindow' is given close the window, otherwise leave with a blank schematic
    -   when closing the last remaining window 
    + if 'force' is given do not ask before closing modified schematic windows/tabs + when closing the last remaining window + This command returns the list of remaining open windows in addition to main window
  • expandlabel lab
  •     Expand vectored labels/instance names:
        xschem expandlabel {2*A[3:0]} --> A[3],A[2],A[1],A[0],A[3],A[2],A[1],A[0] 8
    @@ -853,7 +860,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        
  • new_process [f]
  •     Start a new xschem process for a schematic.
        If 'f' is given load specified schematic. 
    -
  • new_schematic create|destroy|destroy_all|switch_win|switch_tab winpath file
  • +   
  • new_schematic create|destroy|destroy_all|switch winpath file
  •     Open/destroy a new tab or window 
          create: create new empty window or with 'file' loaded if 'file' given.
                  The winpath must be given (even {} is ok).
    @@ -862,8 +869,7 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
          destroy: destroy tab/window identified by winpath. Example:
                   xschem new_schematic destroy .x1.drw
          destroy_all: close all tabs/additional windows
    -     switch_win: switch context to specified 'winpath' window
    -     switch_tab: switch context to specified 'winpath' tab
    +     switch: switch context to specified 'winpath' window or specified schematic name
        Main window/tab has winpath set to .drw,
        Additional windows/tabs have winpath set to .x1.drw, .x2.drw and so on...
  • only_probes
  • @@ -1113,6 +1119,9 @@ C {verilog_timescale.sym} 1050 -100 0 0 {name=s1 timestep="1ns" precision="1ns"
        When a symbol is selected edit it in a new tab/window if not already open.
        If nothing selected open another window of the second schematic (issues a warning).
        if 'new_process' is given start a new xschem process 
    +
  • switch [window_path |schematic_name]
  • +   Switch context to indicated window path or schematic name
    +   returns the # of windows/tabs in addition to main window/tab
  • symbols [n]
  •     if 'n' given list symbol with name or number 'n', else 
        list all used symbols 
    diff --git a/src/callback.c b/src/callback.c index 675c1848..95d86d9e 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1033,12 +1033,11 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, if( xctx->semaphore >= 1 || event == Expose) { dbg(1, "callback(): semaphore >=2 (or Expose) switching window context: %s --> %s\n", old_winpath, winpath); redraw_only = 1; + new_schematic("switch_no_tcl_ctx", winpath, ""); } else { dbg(1, "callback(): switching window context: %s --> %s, semaphore=%d\n", old_winpath, winpath, xctx->semaphore); - if(old_winpath[0]) tclvareval("save_ctx ", old_winpath, NULL); - tclvareval("restore_ctx ", winpath, NULL); + new_schematic("switch", winpath, ""); } - new_schematic("switch_win", winpath, ""); tclvareval("housekeeping_ctx", NULL); } /* artificially set semaphore to allow only redraw operations in switched schematic, @@ -2863,7 +2862,7 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, if(redraw_only) { xctx->semaphore--; /* decrement articially incremented semaphore (see above) */ dbg(1, "callback(): semaphore >=2 restoring window context: %s <-- %s\n", old_winpath, winpath); - if(old_winpath[0]) new_schematic("switch_win", old_winpath, ""); + if(old_winpath[0]) new_schematic("switch_no_tcl_ctx", old_winpath, ""); } else if(strcmp(old_winpath, winpath)) { diff --git a/src/scheduler.c b/src/scheduler.c index 19564f79..24b8daae 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -571,6 +571,21 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } + /* destroy_all [force] + * Close all additional windows/.tabs. If 'force' is given do not ask for + * confirmation for changed schematics + * Returns the remaining # of windows/tabs in addition to main window/tab */ + else if(!strcmp(argv[1], "destroy_all")) + { + int force = 0; + if(argc > 2 && !strcmp(argv[2], "force")) force = 1; + if(force) + new_schematic("destroy_all", "force", NULL); + else + new_schematic("destroy_all", NULL, NULL); + Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); + } + /* display_hilights [nets|instances] * Print a list of highlighted objects (nets, net labels/pins, instances) * if 'instances' is specified list only instance highlights @@ -683,12 +698,18 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg /* exit [closewindow] * Exit the program, ask for confirm if current file modified. * if 'closewindow' is given close the window, otherwise leave with a blank schematic - * when closing the last remaining window */ + * if 'force' is given do not ask before closing modified schematic windows/tabs + * when closing the last remaining window + * This command returns the list of remaining open windows in addition to main window */ else if(!strcmp(argv[1], "exit")) { int closewindow = 0; + int force = 0; - if(argc > 2 && !strcmp(argv[2], "closewindow")) closewindow = 1; + for(i = 2; i < argc; ++i) { + if(!strcmp(argv[i], "closewindow")) closewindow = 1; + if(!strcmp(argv[i], "force")) force = 1; + } if(!strcmp(xctx->current_win_path, ".drw")) { if(has_x) { /* non tabbed interface */ @@ -696,23 +717,23 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg int wc = get_window_count(); dbg(1, "wc=%d\n", wc); if(wc > 0 ) { - if(xctx->modified) { + if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } - if(!xctx->modified || !strcmp(tclresult(), "ok")) { + if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { swap_windows(); set_modify(0); /* set modified status to 0 to avoid another confirm in following line */ new_schematic("destroy", xctx->current_win_path, NULL); } } else { - if(xctx->modified) { + if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } - if(!xctx->modified || !strcmp(tclresult(), "ok")) { + if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { if(closewindow) tcleval("exit"); else clear_schematic(0, 0); } @@ -723,23 +744,23 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg int wc = get_window_count(); dbg(1, "wc=%d\n", wc); if(wc > 0 ) { - if(xctx->modified) { + if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } - if(!xctx->modified || !strcmp(tclresult(), "ok")) { + if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { swap_tabs(); set_modify(0); new_schematic("destroy", xctx->current_win_path, NULL); } } else { - if(xctx->modified) { + if(!force && xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" ": UNSAVED data: want to exit?\""); } - if(!xctx->modified || !strcmp(tclresult(), "ok")) { + if(force || !xctx->modified || !strcmp(tclresult(), "ok")) { if(closewindow) tcleval("exit"); else clear_schematic(0, 0); } @@ -747,10 +768,11 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } } else tcleval("exit"); /* if has_x == 0 there are no additional windows to close */ - } else { + } else { + if(force) set_modify(0); /* avoid ask to save downstream */ new_schematic("destroy", xctx->current_win_path, NULL); } - Tcl_ResetResult(interp); + Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } /* expandlabel lab @@ -2229,7 +2251,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } - /* new_schematic create|destroy|destroy_all|switch_win|switch_tab winpath file + /* new_schematic create|destroy|destroy_all|switch winpath file * Open/destroy a new tab or window * create: create new empty window or with 'file' loaded if 'file' given. * The winpath must be given (even {} is ok). @@ -2238,8 +2260,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg * destroy: destroy tab/window identified by winpath. Example: * xschem new_schematic destroy .x1.drw * destroy_all: close all tabs/additional windows - * switch_win: switch context to specified 'winpath' window - * switch_tab: switch context to specified 'winpath' tab + * switch: switch context to specified 'winpath' window or specified schematic name * Main window/tab has winpath set to .drw, * Additional windows/tabs have winpath set to .x1.drw, .x2.drw and so on... */ @@ -3669,6 +3690,16 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } + /* switch [window_path |schematic_name] + * Switch context to indicated window path or schematic name + * returns the # of windows/tabs in addition to main window/tab + */ + else if(!strcmp(argv[1], "switch")) + { + if(argc > 2) new_schematic("switch", argv[2], NULL); + Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); + } + /* symbols [n] * if 'n' given list symbol with name or number 'n', else * list all used symbols */ diff --git a/src/xinit.c b/src/xinit.c index a1fa93bb..acc2581f 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -1183,6 +1183,33 @@ void preview_window(const char *what, const char *win_path, const char *fname) semaphore--; } + +static int get_tab_or_window_number(const char *win_path) +{ + int i, n = -1; + Xschem_ctx *ctx, **save_xctx = get_save_xctx(); + for(i = 0; i < MAX_NEW_WINDOWS; ++i) { + if(!strcmp(win_path, window_path[i])) { + n = i; + break; + } + } + if(n == -1) { + for(i = 0; i < MAX_NEW_WINDOWS; ++i) { + ctx = save_xctx[i]; + /* if only one schematic it is not yet saved in save_xctx */ + if(get_window_count() == 0 && i == 0) { + ctx = xctx; + } + if(ctx && !strcmp(win_path, get_cell_w_ext(ctx->current_name, 0))) { + n = i; + break; + } + } + } + return n; +} + /* swap primary view (.drw) with first valid tab (x1.drw, x2.drw, ...) * used for window close ('xschem exit' command) */ void swap_tabs(void) @@ -1304,7 +1331,7 @@ void swap_windows(void) /* rebuld colors and pixmaps, redraw swapped schematics */ tclvareval("restore_ctx ", wp_i, NULL); - new_schematic("switch_win", wp_i, ""); + new_schematic("switch", wp_i, ""); tclvareval("housekeeping_ctx", NULL); tclvareval("xschem build_colors", NULL); resetwin(1, 1, 1, 0, 0); @@ -1314,7 +1341,7 @@ void swap_windows(void) /* set context to window that is about to be deleted */ tclvareval("restore_ctx ", wp_j, NULL); - new_schematic("switch_win", wp_j, ""); + new_schematic("switch", wp_j, ""); tclvareval("housekeeping_ctx", NULL); tclvareval("xschem build_colors", NULL); resetwin(1, 1, 1, 0, 0); @@ -1353,37 +1380,36 @@ int check_loaded(const char *f, char *win_path) return found; } -/* caller (via new_schematic() ) should take care of switching tcl context - * before calling this function, see callback() - * - * tclvareval("save_ctx ", old_winpath, NULL); - * tclvareval("restore_ctx ", winpath, NULL); - * new_schematic("switch_win", winpath, ""); - * tclvareval("housekeeping_ctx", NULL); - */ -static void switch_window(int *window_count, const char *win_path) +static void switch_window(int *window_count, const char *win_path, int tcl_ctx) { - int i, n; + int n; + char my_win_path[80]; Tk_Window tkwin; + if(!win_path) { + dbg(0, "switch_window(): no filename or window path given\n"); + return; + } if(!strcmp(win_path, xctx->current_win_path)) return; /* already there */ if(*window_count) { - dbg(1, "new_schematic(\"switch_win\"...): %s\n", win_path); - tkwin = Tk_NameToWindow(interp, win_path, mainwindow); /* NULL if win_path not existing */ - if(!tkwin) dbg(0, "new_schematic(\"switch_win\",...): Warning: %s has been destroyed\n", win_path); - n = -1; - if(tkwin) for(i = 0; i < MAX_NEW_WINDOWS; ++i) { - if(!strcmp(win_path, window_path[i])) { - n = i; - break; - } - } + n = get_tab_or_window_number(win_path); if(n == -1) { - dbg(0, "new_schematic(\"switch_win\"...): no window to switch to found: %s\n", win_path); + dbg(0, "new_schematic(\"switch\"...): no window to switch to found: %s\n", win_path); return; } + /* build my_win_path since win_path can also be a filename */ + if(n == 0) my_snprintf(my_win_path, S(my_win_path), ".drw"); + else my_snprintf(my_win_path, S(my_win_path), ".x%d.drw", n); + dbg(1, "new_schematic(\"switch\"...): %s\n", my_win_path); + tkwin = Tk_NameToWindow(interp, my_win_path, mainwindow); /* NULL if win_path not existing */ + if(!tkwin) dbg(0, "new_schematic(\"switch\",...): Warning: %s has been destroyed\n", win_path); /* if window was closed then tkwin == 0 --> do nothing */ if(tkwin && n >= 0 && n < MAX_NEW_WINDOWS) { + + if(tcl_ctx) tclvareval("save_ctx ", xctx->current_win_path, NULL); xctx = save_xctx[n]; + if(tcl_ctx) tclvareval("restore_ctx ", win_path, NULL); + tclvareval("housekeeping_ctx", NULL); + if(tcl_ctx) tclvareval("reconfigure_layers_button {}", NULL); set_modify(-1); /* sets window title */ } } @@ -1391,19 +1417,17 @@ static void switch_window(int *window_count, const char *win_path) static void switch_tab(int *window_count, const char *win_path) { - int i, n; + int n; if(xctx->semaphore) return; /* some editing operation ongoing. do nothing */ + if(!win_path) { + dbg(0, "switch_tab(): no filename or window path given\n"); + return; + } if(!strcmp(win_path, xctx->current_win_path)) return; /* already there */ if(*window_count) { dbg(1, "new_schematic() switch_tab: %s\n", win_path); - n = -1; - for(i = 0; i < MAX_NEW_WINDOWS; ++i) { - if(!strcmp(win_path, window_path[i])) { - n = i; - break; - } - } + n = get_tab_or_window_number(win_path); if(n == -1) { dbg(0, "new_schematic(\"switch_tab\"...): no tab to switch to found: %s\n", win_path); return; @@ -1508,7 +1532,7 @@ static void create_new_window(int *window_count, const char *noconfirm, const ch * because the Expose event after new window creation does a context switch prev win -> new win */ tclvareval("restore_ctx ", prev_window, NULL); - new_schematic("switch_win", prev_window, ""); + new_schematic("switch", prev_window, ""); tclvareval("housekeeping_ctx", NULL); windowid(toppath); @@ -1570,7 +1594,7 @@ static void create_new_tab(int *window_count, const char *noconfirm, const char my_snprintf(nn, S(nn), "%d", i); tclvareval( "button ", ".tabs.x", nn, " -padx 2 -pady 0 -takefocus 0 -anchor nw -text Tab2 " - "-command \"xschem new_schematic switch_tab .x", nn, ".drw\"", NULL); + "-command \"xschem new_schematic switch .x", nn, ".drw\"", NULL); tclvareval("bind .tabs.x",nn," {swap_tabs %X %Y press}", NULL); tclvareval("bind .tabs.x",nn," {swap_tabs %X %Y release}", NULL); tclvareval( @@ -1612,6 +1636,10 @@ static void destroy_window(int *window_count, const char *win_path) if(*window_count) { int close = 0; dbg(1, "new_schematic() destroy {%s}\n", win_path); + if(!win_path) { + dbg(0, "destroy_window(): no window path given\n"); + return; + } if(xctx->modified && has_x) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" @@ -1666,6 +1694,10 @@ static void destroy_tab(int *window_count, const char *win_path) int close = 0; dbg(1, "new_schematic() destroy_tab\n"); + if(!win_path) { + dbg(0, "destroy_tab(): no window path given\n"); + return; + } if(strcmp(win_path, xctx->current_win_path)) { dbg(0, "new_schematic(\"destroy_tab\", %s): must be in this tab to destroy\n", win_path); return; @@ -1844,10 +1876,11 @@ int new_schematic(const char *what, const char *win_path, const char *fname) } else { destroy_all_tabs(&window_count, win_path ? 1 : 0); } - } else if(!strcmp(what, "switch_win")) { - switch_window(&window_count, win_path); /* see comments in switch_window() */ - } else if(!strcmp(what, "switch_tab")) { - switch_tab(&window_count, win_path); + } else if(!strcmp(what, "switch")) { + if(tabbed_interface) switch_tab(&window_count, win_path); + else switch_window(&window_count, win_path, 1); + } else if(!strcmp(what, "switch_no_tcl_ctx") && !tabbed_interface) { + switch_window(&window_count, win_path, 0); } return window_count; } diff --git a/src/xschem.tcl b/src/xschem.tcl index bb277b69..ae0ba5f2 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -5230,7 +5230,7 @@ proc setup_tabbed_interface {} { if { ![winfo exists .tabs] } { frame .tabs button .tabs.x0 -padx 2 -pady 0 -anchor nw -takefocus 0 \ - -text Main -command "xschem new_schematic switch_tab .drw" + -text Main -command "xschem new_schematic switch .drw" bind .tabs.x0 {swap_tabs %X %Y press} bind .tabs.x0 {swap_tabs %X %Y release} button .tabs.add -padx 0 -pady 0 -takefocus 0 -text { + } -command "xschem new_schematic create" @@ -5255,12 +5255,12 @@ proc setup_tabbed_interface {} { save_ctx $old restore_ctx .drw housekeeping_ctx - xschem new_schematic switch_win .drw + xschem new_schematic switch .drw xschem exit closewindow # did not exit (user cancel) ... switch back restore_ctx $old housekeeping_ctx - xschem new_schematic switch_win $old + xschem new_schematic switch $old } } } @@ -5371,12 +5371,12 @@ proc quit_xschem {} { if {$remaining == 0 } { save_ctx [xschem get current_win_path] restore_ctx .drw - xschem new_schematic switch_win .drw + xschem new_schematic switch .drw housekeeping_ctx xschem exit closewindow } } else { - xschem new_schematic switch_tab .drw + xschem new_schematic switch .drw xschem exit closewindow } } @@ -6265,12 +6265,12 @@ tclcommand=\"xschem raw_read \$netlist_dir/[file tail [file rootname [xschem get save_ctx $old restore_ctx .drw housekeeping_ctx - xschem new_schematic switch_win .drw + xschem new_schematic switch .drw xschem exit closewindow # did not exit ... switch back restore_ctx $old housekeeping_ctx - xschem new_schematic switch_win $old + xschem new_schematic switch $old } } else { wm protocol $rootwin WM_DELETE_WINDOW "xschem new_schematic destroy $topwin.drw {}"