diff --git a/src/actions.c b/src/actions.c index 0886613a..dd79462e 100644 --- a/src/actions.c +++ b/src/actions.c @@ -1511,13 +1511,13 @@ void symbol_in_new_window(int new_process) if(xctx->lastsel !=1 || xctx->sel_array[0].type!=ELEMENT) { my_strncpy(filename, xctx->sch[xctx->currsch], S(filename)); if(new_process) new_xschem_process(filename, 1); - else new_schematic("create", NULL, filename); + else new_schematic("create", NULL, filename, 1); } else { my_strncpy(filename, abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""), S(filename)); if(!check_loaded(filename, win_path)) { if(new_process) new_xschem_process(filename, 1); - else new_schematic("create", NULL, filename); + else new_schematic("create", NULL, filename, 1); } } } @@ -1530,7 +1530,7 @@ void schematic_in_new_window(int new_process) rebuild_selected_array(); if(xctx->lastsel !=1 || xctx->sel_array[0].type!=ELEMENT) { if(new_process) new_xschem_process(xctx->sch[xctx->currsch], 0); - else new_schematic("create", NULL, xctx->sch[xctx->currsch]); + else new_schematic("create", NULL, xctx->sch[xctx->currsch], 1); } else { if( /* do not descend if not subcircuit */ @@ -1547,7 +1547,7 @@ void schematic_in_new_window(int new_process) get_sch_from_sym(filename, xctx->inst[xctx->sel_array[0].n].ptr+ xctx->sym, xctx->sel_array[0].n); if(!check_loaded(filename, win_path)) { if(new_process) new_xschem_process(filename, 0); - else new_schematic("create", NULL, filename); + else new_schematic("create", NULL, filename, 1); } } } diff --git a/src/callback.c b/src/callback.c index a0ba080a..c2f1639e 100644 --- a/src/callback.c +++ b/src/callback.c @@ -1099,10 +1099,10 @@ int rstate; /* (reduced state, without ShiftMask) */ 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, ""); + new_schematic("switch_no_tcl_ctx", winpath, "", 1); } else { dbg(1, "callback(): switching window context: %s --> %s, semaphore=%d\n", old_winpath, winpath, xctx->semaphore); - new_schematic("switch", winpath, ""); + new_schematic("switch", winpath, "", 1); } tclvareval("housekeeping_ctx", NULL); } @@ -3025,7 +3025,7 @@ int rstate; /* (reduced state, without ShiftMask) */ 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_no_tcl_ctx", old_winpath, ""); + if(old_winpath[0]) new_schematic("switch_no_tcl_ctx", old_winpath, "", 1); } else if(strcmp(old_winpath, winpath)) { diff --git a/src/scheduler.c b/src/scheduler.c index 66dcad5a..723d8b2d 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -665,9 +665,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2 && !strcmp(argv[2], "force")) force = 1; if(force) - new_schematic("destroy_all", "force", NULL); + new_schematic("destroy_all", "force", NULL, 1); else - new_schematic("destroy_all", NULL, NULL); + new_schematic("destroy_all", NULL, NULL, 1); Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } @@ -817,7 +817,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg 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); + new_schematic("destroy", xctx->current_win_path, NULL, 1); } } else { if(!force && xctx->modified) { @@ -844,7 +844,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg if(!has_x || force || !xctx->modified || !strcmp(tclresult(), "ok")) { swap_tabs(); set_modify(0); - new_schematic("destroy", xctx->current_win_path, NULL); + new_schematic("destroy", xctx->current_win_path, NULL, 1); } } else { if(has_x && !force && xctx->modified) { @@ -860,7 +860,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } } else { if(force) set_modify(0); /* avoid ask to save downstream */ - new_schematic("destroy", xctx->current_win_path, NULL); + new_schematic("destroy", xctx->current_win_path, NULL, 1); } Tcl_SetResult(interp, my_itoa(get_window_count()), TCL_VOLATILE); } @@ -2322,10 +2322,10 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg } if(!cancel) { if(f[0]) { - new_schematic("create", "noconfirm", f); + new_schematic("create", "noconfirm", f, 1); tclvareval("update_recent_file {", f, "}", NULL); } else { - new_schematic("create", NULL, NULL); + new_schematic("create", NULL, NULL, 1); } } Tcl_ResetResult(interp); @@ -2688,7 +2688,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg Tcl_ResetResult(interp); } - /* new_schematic create|destroy|destroy_all|switch winpath file + /* new_schematic create|destroy|destroy_all|switch winpath file [draw] * 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). @@ -2706,18 +2706,20 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[1], "new_schematic")) { int r = -1; + int dr = 1; char s[20]; if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} if(argc > 2) { - if(argc == 3) r = new_schematic(argv[2], NULL, NULL); - else if(argc == 4) r = new_schematic(argv[2], argv[3], NULL); - else if(argc == 5) { + if(argc >= 6 && argv[5][0] == '0') dr = 0; + if(argc == 3) r = new_schematic(argv[2], NULL, NULL, 1); + else if(argc == 4) r = new_schematic(argv[2], argv[3], NULL, 1); + else if(argc >= 5) { char f[PATH_MAX + 100]; my_snprintf(f, S(f),"regsub {^~/} {%s} {%s/}", argv[4], home_dir); tcleval(f); my_strncpy(f, abs_sym_path(tclresult(), ""), S(f)); - r = new_schematic(argv[2], argv[3], f); + r = new_schematic(argv[2], argv[3], f, dr); } my_snprintf(s, S(s), "%d", r); Tcl_SetResult(interp, s, TCL_VOLATILE); @@ -4468,7 +4470,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg { int r = 1; /* error: no switch was done */ if(!xctx) {Tcl_SetResult(interp, not_avail, TCL_STATIC); return TCL_ERROR;} - if(argc > 2) r = new_schematic("switch", argv[2], NULL); + if(argc > 2) r = new_schematic("switch", argv[2], NULL, 1); Tcl_SetResult(interp, my_itoa(r), TCL_VOLATILE); } diff --git a/src/xinit.c b/src/xinit.c index 6f888d2b..66253e53 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -83,6 +83,12 @@ Xschem_ctx **get_save_xctx(void) return save_xctx; } +/* array of tab names to be matched with xctx->current_win_path + * window_path[0] = .drw + * window_path[1] = .x3.drw + * window_path[2] = .x4.drw + * ... + */ char *get_window_path(int i) { return window_path[i]; @@ -864,7 +870,7 @@ static void xwin_exit(void) tcleval("catch { ngspice::resetdata }"); /* remove ngspice annotation data if any */ /* "1" parameter means to force exit even if there are modified tabs/windows */ if(xctx->infowindow_text) my_free(_ALLOC_ID_, &xctx->infowindow_text); - if(has_x) new_schematic("destroy_all", "1", NULL); + if(has_x) new_schematic("destroy_all", "1", NULL, 1); delete_schematic_data(1); if(has_x) { Tk_DestroyWindow(mainwindow); @@ -1211,10 +1217,11 @@ static int get_tab_or_window_number(const char *win_path) } 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; + } else { + ctx = save_xctx[i]; } if(ctx && !strcmp(win_path, get_cell_w_ext(ctx->current_name, 0))) { n = i; @@ -1269,7 +1276,7 @@ void swap_tabs(void) xctx = ctx; set_modify(-1); /* set context to tab that is about to be deleted */ - new_schematic("switch_tab", save_xctx[j]->current_win_path, NULL); + new_schematic("switch_tab", save_xctx[j]->current_win_path, NULL, 1); } } @@ -1346,7 +1353,7 @@ void swap_windows(void) /* rebuld colors and pixmaps, redraw swapped schematics */ tclvareval("restore_ctx ", wp_i, NULL); - new_schematic("switch", wp_i, ""); + new_schematic("switch", wp_i, "", 1); tclvareval("housekeeping_ctx", NULL); tclvareval("xschem build_colors", NULL); resetwin(1, 1, 1, 0, 0); @@ -1356,7 +1363,7 @@ void swap_windows(void) /* set context to window that is about to be deleted */ tclvareval("restore_ctx ", wp_j, NULL); - new_schematic("switch", wp_j, ""); + new_schematic("switch", wp_j, "", 1); tclvareval("housekeeping_ctx", NULL); tclvareval("xschem build_colors", NULL); resetwin(1, 1, 1, 0, 0); @@ -1368,6 +1375,9 @@ void swap_windows(void) /* check if filename is already loaded into a tab or window */ /* caller should supply a win_path string for storing matching window path */ +/* window_path[0] == ".drw" */ +/* window_path[1] == ".x1.drw" */ +/* .... */ int check_loaded(const char *f, char *win_path) { int i; @@ -1395,11 +1405,13 @@ int check_loaded(const char *f, char *win_path) return found; } +/* win_path: .drw for main (first) window, .x1.drw, ... for additional windows */ static int switch_window(int *window_count, const char *win_path, int tcl_ctx) { int n; char my_win_path[80]; Tk_Window tkwin=NULL; + dbg(1, "switch_window(): win_path=%s\n", win_path); if(xctx->semaphore) return 1; /* some editing operation ongoing. do nothing */ if(!win_path) { dbg(0, "switch_window(): no filename or window path given\n"); @@ -1438,7 +1450,8 @@ static int switch_window(int *window_count, const char *win_path, int tcl_ctx) return 0; } -static int switch_tab(int *window_count, const char *win_path) +/* win_path: .drw for main (first) tab, .x1.drw, ... for additional tabs */ +static int switch_tab(int *window_count, const char *win_path, int dr) { int n; @@ -1464,9 +1477,9 @@ static int switch_tab(int *window_count, const char *win_path) tclvareval("housekeeping_ctx", NULL); if(has_x) tclvareval("reconfigure_layers_button {}", NULL); xctx->window = save_xctx[0]->window; - resetwin(1, 1, 1, 0, 0); + if(dr) resetwin(1, 1, 1, 0, 0); set_modify(-1); /* sets window title */ - draw(); + if(dr) draw(); return 0; } else return 1; } @@ -1565,7 +1578,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", prev_window, ""); + new_schematic("switch", prev_window, "", 1); tclvareval("housekeeping_ctx", NULL); if(has_x) windowid(toppath); @@ -1588,13 +1601,13 @@ static void create_new_tab(int *window_count, const char *noconfirm, const char if(has_x) { tcleval(msg); if(strcmp(tclresult(), "ok")) { - switch_tab(window_count, open_path); + switch_tab(window_count, open_path, 1); return; } } else { dbg(0, "create_new_tab: %s already open: %s\n", fname, open_path); - switch_tab(window_count, open_path); + switch_tab(window_count, open_path, 1); return; } } @@ -1631,8 +1644,9 @@ static void create_new_tab(int *window_count, const char *noconfirm, const char tclvareval( "button ", ".tabs.x", nn, " -padx 2 -pady 0 -takefocus 0 -anchor nw -text Tab2 " "-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("bind .tabs.x",nn," {swap_tabs %X %Y press}", NULL); + tclvareval("bind .tabs.x",nn," {swap_tabs %X %Y release}", NULL); + tclvareval("bind .tabs.x",nn," {tab_context_menu %W}", NULL); tclvareval( "if {![info exists tctx::tab_bg] } {set tctx::tab_bg [", ".tabs.x", nn, " cget -bg]}", NULL); @@ -1895,7 +1909,7 @@ static void destroy_all_tabs(int *window_count, int force) * ".drw" "" * ".x1.drw" ".x1" */ -int new_schematic(const char *what, const char *win_path, const char *fname) +int new_schematic(const char *what, const char *win_path, const char *fname, int dr) { int tabbed_interface; @@ -1922,7 +1936,7 @@ int new_schematic(const char *what, const char *win_path, const char *fname) destroy_all_tabs(&window_count, (win_path && win_path[0]) ? 1 : 0); } } else if(!strcmp(what, "switch")) { - if(tabbed_interface) return switch_tab(&window_count, win_path); + if(tabbed_interface) return switch_tab(&window_count, win_path, dr); else return switch_window(&window_count, win_path, 1); } else if(!strcmp(what, "switch_no_tcl_ctx") && !tabbed_interface) { switch_window(&window_count, win_path, 0); diff --git a/src/xschem.h b/src/xschem.h index 0be7c974..a288b5c8 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -1634,7 +1634,7 @@ extern void change_layer(); extern void launcher(); extern void windowid(const char *winpath); extern void preview_window(const char *what, const char *tk_win_path, const char *fname); -extern int new_schematic(const char *what, const char *win_path, const char *fname); +extern int new_schematic(const char *what, const char *win_path, const char *fname, int dr); extern void toggle_fullscreen(const char *topwin); extern void toggle_only_probes(); extern int build_colors(double dim, double dim_bg); /* reparse the TCL 'colors' list and reassign colors 20171113 */ diff --git a/src/xschem.tcl b/src/xschem.tcl index 1bb38e9d..2fd6d8f6 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -5563,6 +5563,150 @@ proc context_menu { } { return $retval } +proc tab_ctx_cmd {tab_but what} { + global terminal editor netlist_dir + # get win_path from tab name + set win_path [lindex [$tab_but cget -command] 3] ;# xschem new_schematic switch .x1.drw + set tablist [xschem tab_list] + set found 0 + foreach {tabname filename} $tablist { + if {$win_path eq $tabname} { + set found 1 + break + } + } + if {!$found} { set filename {}} + if { $filename ne {} } { + if {$what eq {dir}} { + execute 0 xdg-open [file dirname $filename] + } elseif {$what eq {copy}} { + clipboard clear + clipboard append $filename + } elseif {$what eq {term}} { + set save [pwd] + cd [file dirname $filename] + execute 0 $terminal + cd $save + } elseif {$what eq {edit}} { + eval execute 0 $editor $filename + } elseif {$what eq {netlist}} { + set old [xschem get current_win_path] + set save [pwd] + xschem new_schematic switch $win_path {} 0 ;# no draw + cd $netlist_dir + eval execute 0 $editor [xschem get netlist_name fallback] + cd $save + xschem new_schematic switch $old {} 0 ;# no draw + } elseif {$what eq {close}} { + set old [xschem get current_win_path] + xschem new_schematic switch $win_path {} 0 ;# no draw + xschem exit + xschem new_schematic switch $old {} 1 ;# draw + } + } + # puts $filename +} +proc tab_context_menu {tab_but} { + global retval + + + # find filename associated with tab button + set win_path [lindex [$tab_but cget -command] 3] ;# xschem new_schematic switch .x1.drw + set tablist [xschem tab_list] ;# .drw filename0 .x1.drw filename1 ... + set found 0 + foreach {tabname filename} $tablist { + if {$win_path eq $tabname} { + set found 1 + break + } + } + if {!$found} { set filename {}} + + if {[is_xschem_file $filename] eq {SCHEMATIC}} { + set counterpart [abs_sym_path $filename .sym] + set msg {Open symbol} + } elseif {[is_xschem_file $filename] eq {SYMBOL}} { + set counterpart [abs_sym_path $filename .sch] + set msg {Open schematic} + } else { + set counterpart {} + set msg {} + } + # puts $counterpart + + # puts $tab_but + set retval 0 + if {[info tclversion] >= 8.5} { + set font {Sans 8 bold} + } else { + set font fixed + } + toplevel .ctxmenu + wm overrideredirect .ctxmenu 1 + set x [expr {[winfo pointerx .ctxmenu] - 10}] + set y [expr {[winfo pointery .ctxmenu] - 10}] + button .ctxmenu.b0 -text {Tab menu} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -bg white -highlightthickness 0 -state disabled -disabledforeground black -font [subst $font] + button .ctxmenu.b1 -text {Copy filename} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 1; tab_ctx_cmd $tab_but copy; destroy .ctxmenu" + button .ctxmenu.b2 -text {Open directory} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 2; tab_ctx_cmd $tab_but dir; destroy .ctxmenu" + button .ctxmenu.b3 -text {Open terminal} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 3; tab_ctx_cmd $tab_but term; destroy .ctxmenu" + button .ctxmenu.b4 -text {Edit file} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 4; tab_ctx_cmd $tab_but edit; destroy .ctxmenu" + button .ctxmenu.b5 -text {Edit netlist} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 5; tab_ctx_cmd $tab_but netlist; destroy .ctxmenu" + if {$counterpart ne {}} { + button .ctxmenu.b6 -text $msg -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] \ + -command " + set retval 6 + xschem new_schematic create {} $counterpart + destroy .ctxmenu + " + } + button .ctxmenu.b7 -text {Close tab} -padx 3 -pady 0 -anchor w -activebackground grey50 \ + -highlightthickness 0 -image CtxmenuEdit -compound left \ + -font [subst $font] -command "set retval 7; tab_ctx_cmd $tab_but close; destroy .ctxmenu" + + pack .ctxmenu.b0 -fill x -expand true + pack .ctxmenu.b1 -fill x -expand true + pack .ctxmenu.b2 -fill x -expand true + pack .ctxmenu.b3 -fill x -expand true + pack .ctxmenu.b4 -fill x -expand true + pack .ctxmenu.b5 -fill x -expand true + if {$counterpart ne {}} { + pack .ctxmenu.b6 -fill x -expand true + } + pack .ctxmenu.b7 -fill x -expand true + wm geometry .ctxmenu "+$x+$y" + update + # if window has been destroyed (by mouse pointer exiting) do nothing + if { ![winfo exists .ctxmenu] } { return 0 } + + set wx [winfo width .ctxmenu] + set wy [winfo height .ctxmenu] + set sx [winfo screenwidth .] + set sy [winfo screenheight .] + if { $y + $wy > $sy } { + set y [expr {$y - ( $y + $wy - $sy )} ] + } + if { $x + $wx > $sx } { + set x [expr {$x - ( $x + $wx - $sx )} ] + } + wm geometry .ctxmenu "+$x+$y";# move away from screen edges + bind .ctxmenu {if { {%W} eq {.ctxmenu} } {destroy .ctxmenu}} + tkwait window .ctxmenu + return $retval +} + # # toolbar: Public variables that we allow to be overridden # Code contributed by Neil Johnson (github: nejohnson) @@ -5719,8 +5863,9 @@ proc setup_tabbed_interface {} { frame .tabs button .tabs.x0 -padx 2 -pady 0 -anchor nw -takefocus 0 \ -text Main -command "xschem new_schematic switch .drw" - bind .tabs.x0 {swap_tabs %X %Y press} - bind .tabs.x0 {swap_tabs %X %Y release} + bind .tabs.x0 {tab_context_menu %W} + 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" pack .tabs.x0 .tabs.add -side left balloon .tabs.add {Create a new tab}