From e238f571b077b11aeabe6cf841f1950465347686 Mon Sep 17 00:00:00 2001 From: Stefan Frederik Date: Sun, 9 Jan 2022 05:14:25 +0100 Subject: [PATCH] code added for tabbed window interface --- src/actions.c | 6 +- src/callback.c | 26 +-- src/draw.c | 15 +- src/save.c | 10 +- src/scheduler.c | 38 +++-- src/xinit.c | 436 +++++++++++++++++++++++++++++++++++------------- src/xschem.h | 16 +- src/xschem.tcl | 140 ++++++---------- 8 files changed, 427 insertions(+), 260 deletions(-) diff --git a/src/actions.c b/src/actions.c index 36fc94cf..8949ab21 100644 --- a/src/actions.c +++ b/src/actions.c @@ -75,7 +75,7 @@ unsigned int hash_file(const char *f, int skip_path_lines) return 0; } - +/* mod=-1 used to force set title */ void set_modify(int mod) { char *top_path; @@ -83,7 +83,8 @@ void set_modify(int mod) top_path = xctx->top_path[0] ? xctx->top_path : "."; xctx->modified = mod; dbg(1, "set_modify(): %d\n", mod); - if(mod != xctx->prev_set_modify) { + if(mod == -1 || mod != xctx->prev_set_modify) { /* mod=-1 used to force set title */ + mod = 0; xctx->prev_set_modify = mod; if(has_x && strcmp(get_cell(xctx->sch[xctx->currsch],1), "systemlib/font")) { if(mod == 1) { @@ -95,6 +96,7 @@ void set_modify(int mod) } } } + tcleval("set_tab_names"); } void print_version() diff --git a/src/callback.c b/src/callback.c index 27c557ca..cb5633d6 100644 --- a/src/callback.c +++ b/src/callback.c @@ -809,17 +809,20 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, dbg(1, "callback(): semaphore >=2 (or Expose) switching window context: %s --> %s\n", old_winpath, winpath); redraw_only = 1; } else { - dbg(1, "callback(): switching window context: %s --> %s\n", old_winpath, winpath); + 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); tclvareval("housekeeping_ctx", NULL); } - new_schematic("switch", xctx->top_path, winpath, ""); + new_schematic("switch", winpath, ""); } /* 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) xctx->semaphore++; + if(redraw_only) { + dbg(1, "callback(): incrementing semaphore for redraw_only\n"); + xctx->semaphore++; + } xctx->semaphore++; /* to recognize recursive callback() calls */ @@ -1364,12 +1367,13 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, } if(key=='q' && state == ControlMask) /* exit */ { - char * top_path; - top_path = xctx->top_path[0] ? xctx->top_path : "."; if(xctx->semaphore >= 2) break; if(!strcmp(winpath, ".drw")) { - tcleval("new_window destroy_all"); /* close child schematics */ - if(tclresult()[0] == '1') { + int remaining; + /* tcleval("new_window destroy_all"); */ /* close child schematics */ + remaining = new_schematic("destroy_all", NULL, NULL); + /* if(tclresult()[0] == '1') { */ + if(!remaining) { if(xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" @@ -1379,11 +1383,7 @@ int callback(const char *winpath, int event, int mx, int my, KeySym key, } } else { /* xschem new_schematic destroy asks user confirmation if schematic changed */ - tclvareval("xschem new_schematic destroy ", top_path, " ", winpath," {}" , NULL); - /* ================================================================ */ - /* We must return here, since current schematic is no more existing */ - /* ================================================================ */ - return 0; + new_schematic("destroy", winpath, NULL); } break; } @@ -2565,7 +2565,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", xctx->top_path, old_winpath, ""); + if(old_winpath[0]) new_schematic("switch", old_winpath, ""); } else if(strcmp(old_winpath, winpath)) { diff --git a/src/draw.c b/src/draw.c index 82d0d5b3..f40aea6b 100644 --- a/src/draw.c +++ b/src/draw.c @@ -2268,24 +2268,19 @@ static void show_node_measures(int measure_p, double measure_x, double measure_p int n_bits, int n_nodes, const char *ntok, int wcnt, Graph_ctx *gr) { char tmpstr[1024]; - double yy1; - double diffy; - double diffx; double yy; - char *fmt1, *fmt2; - - - /* show values of signals if cursor1 active */ if(measure_p >= 0) { - int hex_digits = ((n_bits - 1) >> 2) + 1; - double vthl, vthh; /* draw node values in graph */ bbox(START, 0.0, 0.0, 0.0, 0.0); bbox(ADD, gr->rx1, gr->ry1, gr->rx2, gr->ry2); bbox(SET_INSIDE, 0.0, 0.0, 0.0, 0.0); if(!bus_msb) { + double diffy; + double diffx; + char *fmt1, *fmt2; + double yy1; yy1 = xctx->graph_values[idx][measure_p-1]; diffy = xctx->graph_values[idx][measure_p] - yy1; diffx = measure_x - measure_prev_x; @@ -2301,6 +2296,8 @@ static void show_node_measures(int measure_p, double measure_x, double measure_p if(gr->unity != 1.0) my_snprintf(tmpstr, S(tmpstr), fmt2, yy * gr->unity, gr->unity_suffix); else my_snprintf(tmpstr, S(tmpstr), fmt1, yy); } else { + double vthl, vthh; + int hex_digits = ((n_bits - 1) >> 2) + 1; vthh = gr->gy1 * 0.2 + gr->gy2 * 0.8; vthl = gr->gy1 * 0.8 + gr->gy2 * 0.2; get_bus_value(n_bits, hex_digits, idx_arr, measure_p - 1, tmpstr, vthl, vthh); diff --git a/src/save.c b/src/save.c index caa4aebe..1eb45d8d 100644 --- a/src/save.c +++ b/src/save.c @@ -976,22 +976,15 @@ int save_schematic(const char *schname) /* 20171020 added return value */ { FILE *fd; char name[PATH_MAX]; /* overflow safe 20161122 */ - char *top_path; struct stat buf; - top_path = xctx->top_path[0] ? xctx->top_path : "."; - if( strcmp(schname,"") ) my_strncpy(xctx->sch[xctx->currsch], schname, S(xctx->sch[xctx->currsch])); else return 0; dbg(1, "save_schematic(): currsch=%d name=%s\n",xctx->currsch, schname); dbg(1, "save_schematic(): sch[currsch]=%s\n", xctx->sch[xctx->currsch]); dbg(1, "save_schematic(): abs_sym_path=%s\n", abs_sym_path(xctx->sch[xctx->currsch], "")); my_strncpy(name, xctx->sch[xctx->currsch], S(name)); - if(has_x) { - tclvareval("wm title ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL); - tclvareval("wm iconname ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL); - } - + set_modify(-1); if(!stat(name, &buf)) { if(xctx->time_last_modify && xctx->time_last_modify != buf.st_mtime) { tclvareval("ask_save \"Schematic file: ", name, @@ -999,7 +992,6 @@ int save_schematic(const char *schname) /* 20171020 added return value */ if(strcmp(tclresult(), "yes") ) return 0; } } - if(!(fd=fopen(name,"w"))) { fprintf(errfp, "save_schematic(): problems opening file %s \n",name); diff --git a/src/scheduler.c b/src/scheduler.c index 6a60bc86..165d15a3 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -482,10 +482,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg xctx->prep_net_structs=0; xctx->prep_hi_structs=0; if(has_x) { - char *top_path; - top_path = xctx->top_path[0] ? xctx->top_path : "."; - tclvareval("wm title ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL); - tclvareval("wm iconname ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL); + set_modify(-1); } } Tcl_ResetResult(interp); @@ -648,13 +645,12 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[1],"exit")) { - char * top_path; cmd_found = 1; - top_path = xctx->top_path[0] ? xctx->top_path : "."; - if(!strcmp(top_path, ".")) { + if(!strcmp(xctx->top_path, "")) { if(has_x) { - tcleval("new_window destroy_all"); /* close child schematics */ - if(tclresult()[0] == '1') { + int remaining; + remaining = new_schematic("destroy_all", NULL, NULL); + if(!remaining) { if(xctx->modified) { tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" "[get_cell [xschem get schname] 0]" @@ -665,7 +661,7 @@ 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 { - tclvareval("xschem new_schematic destroy ", top_path, " ", xctx->top_path, ".drw {}" , NULL); + new_schematic("destroy", xctx->current_win_path, NULL); } Tcl_ResetResult(interp); } @@ -778,6 +774,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[2],"current_name")) { Tcl_SetResult(interp, xctx->current_name, TCL_VOLATILE); } + else if(!strcmp(argv[2],"current_win_path")) { + Tcl_SetResult(interp, xctx->current_win_path, TCL_VOLATILE); + } else if(!strcmp(argv[2],"currsch")) { char s[30]; /* overflow safe 20161122 */ my_snprintf(s, S(s), "%d",xctx->currsch); @@ -856,8 +855,13 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else Tcl_SetResult(interp, "0",TCL_STATIC); } + else if(!strcmp(argv[2],"ntabs")) { + char s[30]; + my_snprintf(s, S(s), "%d",new_schematic("ntabs", NULL, NULL)); + Tcl_SetResult(interp, s,TCL_VOLATILE); + } else if(!strcmp(argv[2],"pinlayer")) { - char s[30]; /* overflow safe 20161122 */ + char s[30]; my_snprintf(s, S(s), "%d",PINLAYER); Tcl_SetResult(interp, s,TCL_VOLATILE); } @@ -919,6 +923,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg my_snprintf(s, S(s), "%d",TEXTLAYER); Tcl_SetResult(interp, s,TCL_VOLATILE); } + else if(!strcmp(argv[2],"top_path")) { + Tcl_SetResult(interp, xctx->top_path, TCL_VOLATILE); + } else if(!strcmp(argv[2],"topwindow")) { char *top_path; top_path = xctx->top_path[0] ? xctx->top_path : "."; @@ -1570,7 +1577,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg my_snprintf(fullname, S(fullname),"%s", tclresult()); } if( fullname[0] ) { - tclvareval("new_window create {", fullname, "}", NULL); + new_schematic("create", NULL, fullname); tclvareval("update_recent_file {", fullname, "}", NULL); } } @@ -1714,9 +1721,9 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg else if(!strcmp(argv[1],"new_schematic")) { cmd_found = 1; - if(argc == 4) new_schematic(argv[2], argv[3], "{}","{}"); - else if(argc == 5) new_schematic(argv[2], argv[3], argv[4], "{}"); - else if(argc == 6) new_schematic(argv[2], argv[3], argv[4], argv[5]); + if(argc == 3) new_schematic(argv[2], NULL, NULL); + else if(argc == 4) new_schematic(argv[2], argv[3], NULL); + else if(argc == 5) new_schematic(argv[2], argv[3], argv[4]); Tcl_ResetResult(interp); } @@ -2482,6 +2489,7 @@ int xschem(ClientData clientdata, Tcl_Interp *interp, int argc, const char * arg text_svg=atoi(argv[3]); } else if(!strcmp(argv[2],"semaphore")) { + dbg(1, "scheduler(): set semaphore to %s\n", argv[3]); xctx->semaphore=atoi(argv[3]); } else if(!strcmp(argv[2],"sym_txt")) { diff --git a/src/xinit.c b/src/xinit.c index 687a64d8..8f325041 100644 --- a/src/xinit.c +++ b/src/xinit.c @@ -318,7 +318,7 @@ void init_pixdata()/* populate xctx->fill_type array that is used in create_gc() } } -void free_xschem_data() +static void free_xschem_data() { int i; xctx->delete_undo(); @@ -373,6 +373,7 @@ void free_xschem_data() my_free(1123, &xctx->enable_layer); my_free(1121, &xctx->active_layer); my_free(1295, &xctx->top_path); + my_free(1295, &xctx->current_win_path); my_free(1120, &xctx->fill_type); if(xctx->inst_redraw_table) my_free(604, &xctx->inst_redraw_table); my_free(269, &xctx); @@ -401,7 +402,7 @@ void free_gc() } } -void alloc_xschem_data(const char *top_path) +static void alloc_xschem_data(const char *top_path, const char *win_path) { int i, j; @@ -598,7 +599,9 @@ void alloc_xschem_data(const char *top_path) xctx->hide_symbols = 0; xctx->netlist_type = CAD_SPICE_NETLIST; xctx->top_path = NULL; + xctx->current_win_path = NULL; my_strdup2(1296, &xctx->top_path, top_path); + my_strdup2(1462, &xctx->current_win_path, win_path); xctx->fill_type=my_calloc(640, cadlayers, sizeof(int)); xctx->fill_pattern = 1; xctx->draw_window = 0; @@ -894,7 +897,7 @@ int source_tcl_file(char *s) return TCL_OK; } -void preview_window(const char *what, const char *tk_win_path, const char *filename) +void preview_window(const char *what, const char *win_path, const char *filename) { static char *current_file = NULL; static Xschem_ctx *save_xctx = NULL; /* save pointer to current schematic context structure */ @@ -904,8 +907,8 @@ void preview_window(const char *what, const char *tk_win_path, const char *filen dbg(1, "------\n"); if(!strcmp(what, "create")) { - dbg(1, "preview_window() create, save ctx, tk_win_path=%s\n", tk_win_path); - tkpre_window = Tk_NameToWindow(interp, tk_win_path, mainwindow); + dbg(1, "preview_window() create, save ctx, win_path=%s\n", win_path); + tkpre_window = Tk_NameToWindow(interp, win_path, mainwindow); Tk_MakeWindowExist(tkpre_window); pre_window = Tk_WindowId(tkpre_window); save_xctx = xctx; /* save current schematic */ @@ -919,7 +922,7 @@ void preview_window(const char *what, const char *tk_win_path, const char *filen } my_strdup(117, ¤t_file, filename); xctx = NULL; /* reset for preview */ - alloc_xschem_data(".dialog"); /* alloc data into xctx */ + alloc_xschem_data(".dialog", ".dialog.drw"); /* alloc data into xctx */ init_pixdata(); /* populate xctx->fill_type array that is used in create_gc() to set fill styles */ preview_xctx = xctx; preview_xctx->window = pre_window; @@ -950,137 +953,338 @@ void preview_window(const char *what, const char *tk_win_path, const char *filen } -/* top_path is the path prefix of tk_win_path: +/* top_path is the path prefix of win_path: * - * tk_win_path top_path + * win_path top_path * ".drw" "" - * ".xx.drw" ".xx" + * ".x1.drw" ".x1" */ -void new_schematic(const char *what, const char *top_path, const char *tk_win_path, const char *filename) +int new_schematic(const char *what, const char *win_path, const char *filename) { static int cnt = 0; static Xschem_ctx *save_xctx[MAX_NEW_WINDOWS]; /* save pointer to current schematic context structure */ - static Tk_Window tknew_window[MAX_NEW_WINDOWS]; + /* static Tk_Window new_window[MAX_NEW_WINDOWS]; */ + static char new_window[MAX_NEW_WINDOWS][80]; int i, n; - Window new_window; Tk_Window tkwin; + char toppath[80]; + int tabbed_interface; + const char *tmp; - if(!strcmp(what, "create")) { - dbg(1, "new_schematic() create, save ctx tk_win_path=%s\n", tk_win_path); - if(cnt == 0) { - for(i = 0; i < MAX_NEW_WINDOWS; i++) { - save_xctx[i] = NULL; - tknew_window[i] = NULL; - } - save_xctx[0] = xctx; /* save current schematic */ - tknew_window[0] = Tk_NameToWindow(interp, ".drw", mainwindow); - } - if(cnt + 1 >= MAX_NEW_WINDOWS) { - dbg(0, "new_schematic(\"create\"...): no more free slots\n"); - return; /* no more free slots */ - } - cnt++; - n = -1; - for(i = 1; i < MAX_NEW_WINDOWS; i++) { /* search 1st free slot */ - if(save_xctx[i] == NULL) { - n = i; - break; - } - } - if(n == -1) { - dbg(0, "new_schematic(\"create\"...): no more free slots\n"); - return; - } - tknew_window[n] = Tk_NameToWindow(interp, tk_win_path, mainwindow); - Tk_MakeWindowExist(tknew_window[n]); - new_window = Tk_WindowId(tknew_window[n]); - Tk_ChangeWindowAttributes(tknew_window[n], CWBackingStore, &winattr); - dbg(1, "new_schematic() draw\n"); - xctx = NULL; /* reset for preview */ - alloc_xschem_data(top_path); /* alloc data into xctx */ - xctx->netlist_type = CAD_SPICE_NETLIST; /* for new windows start with spice netlist mode */ - tclsetvar("netlist_type","spice"); - init_pixdata();/* populate xctx->fill_type array that is used in create_gc() to set fill styles */ - save_xctx[n] = xctx; - dbg(1, "new_schematic() draw, load schematic\n"); - xctx->window = new_window; - set_snap(0); /* set default value specified in xschemrc as 'snap' else CADSNAP */ - set_grid(0); /* set default value specified in xschemrc as 'grid' else CADGRID */ - create_gc(); - enable_layers(); - build_colors(0.0, 0.0); - resetwin(1, 0, 1, 0, 0); /* create preview pixmap. resetwin(create_pixmap, clear_pixmap, force, w, h) */ - /* draw empty window so if following load fails due to missing file window appears correctly drawn */ - zoom_full(1, 0, 1, 0.97); - load_schematic(1,filename, 1); - zoom_full(1, 0, 1, 0.97); /* draw */ - } else if(!strcmp(what, "destroy")) { - if(cnt) { - int close = 0; - dbg(1, "new_schematic() destroy\n"); - /* reset old focused window so callback() will force repaint on expose events */ - if(xctx->modified && has_x) { - tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" - "[get_cell [xschem get schname] 0]" - ": UNSAVED data: want to exit?\""); - if(strcmp(tclresult(),"ok")==0) close = 1; - } - else close = 1; - Tcl_ResetResult(interp); - if(close) { - tkwin = Tk_NameToWindow(interp, tk_win_path, mainwindow); /* NULL if tk_win_path not existing */ - if(!tkwin) dbg(0, "new_schematic(\"destroy\", ...): Warning: %s has been destroyed\n", tk_win_path); - n = -1; - if(tkwin) for(i = 1; i < MAX_NEW_WINDOWS; i++) { - if(tkwin == tknew_window[i]) { - n = i; - break; - } - } - if(n == -1) { - dbg(0, "new_schematic(\"destroy\"...): no window to destroy found: %s\n", tk_win_path); - return; - } - if(tkwin && n >= 1 && n < MAX_NEW_WINDOWS) { - xctx = save_xctx[n]; - delete_schematic_data(); - save_xctx[n] = NULL; - tclvareval("winfo toplevel ", tk_win_path, NULL); - Tk_DestroyWindow(tknew_window[n]); - tclvareval("destroy ", tclresult(), NULL); - tknew_window[n] = NULL; - /* delete Tcl context of deleted schematic window */ - tclvareval("delete_ctx ", tk_win_path, "; incr tctx::cnt -1", NULL); - cnt--; + tabbed_interface = 0; + if((tmp = tclgetvar("tabbed_interface"))) { + if(tmp[0] == '1') tabbed_interface = 1; + } + dbg(1, "new_schematic(): current_win_path=%s, what=%s, win_path=%s\n", xctx->current_win_path, what, win_path); + /********************** NTABS **********************/ + if(!strcmp(what, "ntabs")) { + return cnt; + } else if(!strcmp(what, "create")) { + /********************** CREATE **********************/ + if(!tabbed_interface) { + Window win_id; + dbg(1, "new_schematic() create\n"); + if(cnt == 0) { + for(i = 0; i < MAX_NEW_WINDOWS; i++) { + save_xctx[i] = NULL; + my_strncpy(new_window[i], "", S(new_window[i])); } + tcleval("save_ctx .drw"); + save_xctx[0] = xctx; /* save current schematic */ + /* new_window[0] = Tk_NameToWindow(interp, ".drw", mainwindow); */ + my_strncpy(new_window[0], ".drw", S(new_window[0])); } - xctx = save_xctx[0]; /* restore main (.drw) schematic */ - tcleval("restore_ctx .drw; housekeeping_ctx"); - set_modify(xctx->modified); /* sets window title */ - } - } else if(!strcmp(what, "switch")) { - if(cnt) { - dbg(1, "new_schematic() switch: %s\n", tk_win_path); - tkwin = Tk_NameToWindow(interp, tk_win_path, mainwindow); /* NULL if tk_win_path not existing */ - if(!tkwin) dbg(0, "new_schematic(\"switch\",...): Warning: %s has been destroyed\n", tk_win_path); + if(cnt + 1 >= MAX_NEW_WINDOWS) { + dbg(0, "new_schematic(\"create\"...): no more free slots\n"); + return cnt; /* no more free slots */ + } + cnt++; n = -1; - if(tkwin) for(i = 0; i < MAX_NEW_WINDOWS; i++) { - if(tkwin == tknew_window[i]) { + for(i = 1; i < MAX_NEW_WINDOWS; i++) { /* search 1st free slot */ + if(save_xctx[i] == NULL) { n = i; break; } } if(n == -1) { - dbg(0, "new_schematic(\"switch\"...): no window to switch to found: %s\n", tk_win_path); - return; + dbg(0, "new_schematic(\"create\"...): no more free slots\n"); + return cnt; } - /* if window was closed then tkwin == 0 --> do nothing */ - if(tkwin && n >= 0 && n < MAX_NEW_WINDOWS) { - xctx = save_xctx[n]; + my_snprintf(new_window[n], S(new_window[n]), ".x%d.drw", n); + my_snprintf(toppath, S(toppath), ".x%d", n); + tclvareval("toplevel ", toppath, " -bg {} -width 400 -height 400", NULL); + tclvareval("build_widgets ", toppath, NULL); + tclvareval("pack_widgets ", toppath, " ; update", NULL); + Tk_MakeWindowExist(Tk_NameToWindow(interp, new_window[n], mainwindow)); + win_id = Tk_WindowId(Tk_NameToWindow(interp, new_window[n], mainwindow)); + Tk_ChangeWindowAttributes(Tk_NameToWindow(interp, new_window[n], mainwindow), CWBackingStore, &winattr); + dbg(1, "new_schematic() draw\n"); + xctx = NULL; /* reset for preview */ + alloc_xschem_data(toppath, new_window[n]); /* alloc data into xctx */ + xctx->netlist_type = CAD_SPICE_NETLIST; /* for new windows start with spice netlist mode */ + tclsetvar("netlist_type","spice"); + init_pixdata();/* populate xctx->fill_type array that is used in create_gc() to set fill styles */ + save_xctx[n] = xctx; + dbg(1, "new_schematic() draw, load schematic\n"); + xctx->window = win_id; + set_snap(0); /* set default value specified in xschemrc as 'snap' else CADSNAP */ + set_grid(0); /* set default value specified in xschemrc as 'grid' else CADGRID */ + create_gc(); + enable_layers(); + build_colors(0.0, 0.0); + resetwin(1, 0, 1, 0, 0); /* create preview pixmap. resetwin(create_pixmap, clear_pixmap, force, w, h) */ + /* draw empty window so if following load fails due to missing file window appears correctly drawn */ + zoom_full(1, 0, 1, 0.97); + load_schematic(1,filename, 1); + zoom_full(1, 0, 1, 0.97); /* draw */ + tclvareval("set_bindings ", new_window[n], NULL); + tclvareval("save_ctx ", new_window[n], NULL); + windowid(toppath); + /********************** NEW_TAB **********************/ + } else { + dbg(1, "new_schematic() new_tab, creating...\n"); + if(cnt == 0) { + for(i = 0; i < MAX_NEW_WINDOWS; i++) { + save_xctx[i] = NULL; + my_strncpy(new_window[i], "", S(new_window[i])); + } + tcleval("save_ctx .drw"); + save_xctx[0] = xctx; /* save current schematic */ + my_strncpy(new_window[0], ".drw", S(new_window[0])); + } + if(cnt + 1 >= MAX_NEW_WINDOWS) { + dbg(0, "new_schematic(\"new_tab\"...): no more free slots\n"); + return cnt; /* no more free slots */ + } + cnt++; + n = -1; + for(i = 1; i < MAX_NEW_WINDOWS; i++) { /* search 1st free slot */ + if(save_xctx[i] == NULL) { + n = i; + break; + } + } + if(n == -1) { + dbg(0, "new_schematic(\"newtab\"...): no more free slots\n"); + return cnt; + } + my_strncpy(new_window[n], win_path, S(new_window[n])); + xctx = NULL; /* reset for preview */ + alloc_xschem_data("", win_path); /* alloc data into xctx */ + xctx->netlist_type = CAD_SPICE_NETLIST; /* for new windows start with spice netlist mode */ + tclsetvar("netlist_type","spice"); + init_pixdata();/* populate xctx->fill_type array that is used in create_gc() to set fill styles */ + save_xctx[n] = xctx; + dbg(1, "new_schematic() draw, load schematic\n"); + xctx->window = save_xctx[0]->window; + set_snap(0); /* set default value specified in xschemrc as 'snap' else CADSNAP */ + set_grid(0); /* set default value specified in xschemrc as 'grid' else CADGRID */ + create_gc(); + enable_layers(); + build_colors(0.0, 0.0); + resetwin(1, 0, 1, 0, 0); /* create pixmap. resetwin(create_pixmap, clear_pixmap, force, w, h) */ + /* draw empty window so if following load fails due to missing file window appears correctly drawn */ + zoom_full(1, 0, 1, 0.97); + load_schematic(1,filename, 1); + zoom_full(1, 0, 1, 0.97); /* draw */ + } + } else if(!strcmp(what, "destroy")) { + /********************** DESTROY **********************/ + if(!tabbed_interface) { + Xschem_ctx *savectx; + savectx = xctx; + if(cnt) { + int close = 0; + dbg(1, "new_schematic() destroy {%s}\n", win_path); + if(xctx->modified && has_x) { + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" + "[get_cell [xschem get schname] 0]" + ": UNSAVED data: want to exit?\""); + if(strcmp(tclresult(),"ok")==0) close = 1; + } + else close = 1; + Tcl_ResetResult(interp); + if(close) { + tkwin = Tk_NameToWindow(interp, win_path, mainwindow); /* NULL if win_path not existing */ + if(!tkwin) dbg(0, "new_schematic(\"destroy\", ...): Warning: %s has been destroyed\n", win_path); + n = -1; + if(tkwin) for(i = 1; i < MAX_NEW_WINDOWS; i++) { + if(!strcmp(win_path, new_window[i])) { + n = i; + break; + } + } + if(n == -1) { + dbg(0, "new_schematic(\"destroy\"...): no window to destroy found: %s\n", win_path); + return cnt; + } + if(tkwin && n >= 1 && n < MAX_NEW_WINDOWS) { + xctx = save_xctx[n]; + /* set saved ctx to main window if current is to be destroyed */ + if(savectx == xctx) savectx = save_xctx[0]; + tclvareval("winfo toplevel ", win_path, NULL); + delete_schematic_data(); + save_xctx[n] = NULL; + Tk_DestroyWindow(Tk_NameToWindow(interp, new_window[n], mainwindow)); + tclvareval("destroy ", tclresult(), NULL); + my_strncpy(new_window[n], "", S(new_window[n])); + /* delete Tcl context of deleted schematic window */ + tclvareval("delete_ctx ", win_path, NULL); + cnt--; + } + } + /* following 3 lines must be done also if window not closed */ + xctx = savectx; /* restore previous schematic or main window if previous destroyed */ + tcleval("restore_ctx .drw; housekeeping_ctx"); set_modify(xctx->modified); /* sets window title */ } + /********************** DESTROY_TAB **********************/ + } else { + if(cnt) { + int close = 0; + dbg(1, "new_schematic() destroy_tab\n"); + + 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 cnt; + } + if(xctx->modified && has_x) { + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" + "[get_cell [xschem get schname] 0]" + ": UNSAVED data: want to exit?\""); + if(strcmp(tclresult(),"ok")==0) close = 1; + } + else close = 1; + Tcl_ResetResult(interp); + if(close) { + n = -1; + for(i = 1; i < MAX_NEW_WINDOWS; i++) { + if(!strcmp(win_path, new_window[i])) { + n = i; + break; + } + } + if(n == -1) { + dbg(0, "new_schematic(\"destroy_tab\"...): no tab to destroy found: %s\n", win_path); + return cnt; + } + if(n >= 1 && n < MAX_NEW_WINDOWS) { + xctx = save_xctx[n]; + delete_schematic_data(); + save_xctx[n] = NULL; + my_strncpy(new_window[n], "", S(new_window[n])); + /* delete Tcl context of deleted schematic window */ + tclvareval("delete_ctx ", win_path, NULL); + cnt--; + } + xctx = save_xctx[0]; /* restore main (.drw) schematic */ + tcleval("restore_ctx .drw; housekeeping_ctx"); + set_modify(xctx->modified); /* sets window title */ + draw(); + } + } else { + dbg(0, "new_schematic() destroy_tab: there are no additional tabs\n"); + } + } + /********************** DESTROY_ALL **********************/ + } else if(!strcmp(what, "destroy_all")) { + Xschem_ctx *savectx; + savectx = xctx; + if(cnt) { + int close; + dbg(1, "new_schematic() destroy_all\n"); + for(i = 1; i < MAX_NEW_WINDOWS; i++) { + if(new_window[i][0]) { + tkwin = Tk_NameToWindow(interp, new_window[i], mainwindow); /* NULL if win_path not existing */ + if(!tkwin) dbg(0, "new_schematic(\"switch\",...): Warning: %s has been destroyed\n", new_window[i]); + else { + xctx = save_xctx[i]; + close = 0; + /* reset old focused window so callback() will force repaint on expose events */ + if(xctx->modified && has_x) { + tcleval("tk_messageBox -type okcancel -parent [xschem get topwindow] -message \"" + "[get_cell [xschem get schname] 0]" + ": UNSAVED data: want to exit?\""); + if(strcmp(tclresult(),"ok")==0) close = 1; + } + else close = 1; + Tcl_ResetResult(interp); + if(close) { + tclvareval("winfo toplevel ", new_window[i], NULL); + delete_schematic_data(); + /* set saved ctx to main window if previous is about to be destroyed */ + if(savectx == save_xctx[i]) savectx = save_xctx[0]; + save_xctx[i] = NULL; + Tk_DestroyWindow(Tk_NameToWindow(interp, new_window[i], mainwindow)); + tclvareval("destroy ", tclresult(), NULL); + /* delete Tcl context of deleted schematic window */ + tclvareval("delete_ctx ", new_window[i], NULL); + my_strncpy(new_window[i], "", S(new_window[i])); + cnt--; + } + } + } + } + } + /* following 3 lines must be done also if windows not closed */ + xctx = savectx; /* restore previous schematic or main if old is destroyed */ + tcleval("restore_ctx .drw; housekeeping_ctx"); + set_modify(xctx->modified); /* sets window title */ + } else if(!strcmp(what, "switch")) { + /********************** SWITCH **********************/ + if(!tabbed_interface) { + if(cnt) { + dbg(1, "new_schematic() switch: %s\n", win_path); + tkwin = Tk_NameToWindow(interp, win_path, mainwindow); /* NULL if win_path not existing */ + if(!tkwin) dbg(0, "new_schematic(\"switch\",...): Warning: %s has been destroyed\n", win_path); + n = -1; + if(tkwin) for(i = 0; i < MAX_NEW_WINDOWS; i++) { + if(!strcmp(win_path, new_window[i])) { + n = i; + break; + } + } + if(n == -1) { + dbg(0, "new_schematic(\"switch\"...): no window to switch to found: %s\n", win_path); + return cnt; + } + /* if window was closed then tkwin == 0 --> do nothing */ + if(tkwin && n >= 0 && n < MAX_NEW_WINDOWS) { + xctx = save_xctx[n]; + set_modify(xctx->modified); /* sets window title */ + } + } + /********************** SWITCH_TAB **********************/ + } else { + if(cnt) { + dbg(1, "new_schematic() switch_tab: %s\n", win_path); + n = -1; + for(i = 0; i < MAX_NEW_WINDOWS; i++) { + if(!strcmp(win_path, new_window[i])) { + n = i; + break; + } + } + if(n == -1) { + dbg(0, "new_schematic(\"switch_tab\"...): no tab to switch to found: %s\n", win_path); + return cnt; + } + /* if window was closed then tkwin == 0 --> do nothing */ + if(n >= 0 && n < MAX_NEW_WINDOWS) { + tclvareval("save_ctx ", xctx->current_win_path, NULL); + xctx = save_xctx[n]; + tclvareval("restore_ctx ", win_path, NULL); + tclvareval("housekeeping_ctx", NULL); + tclvareval("reconfigure_layers_button {}", NULL); + xctx->window = save_xctx[0]->window; + resetwin(1, 1, 1, 0, 0); + set_modify(xctx->modified); /* sets window title */ + draw(); + } + } } } + return cnt; } void change_linewidth(double w) @@ -1601,7 +1805,7 @@ int Tcl_AppInit(Tcl_Interp *inter) /* */ /* [m]allocate dynamic memory */ /* */ - alloc_xschem_data(""); + alloc_xschem_data("", ".drw"); /* global context / graphic preferences/settings */ pixdata=my_calloc(641, cadlayers, sizeof(char*)); diff --git a/src/xschem.h b/src/xschem.h index f279e30a..86817eaa 100644 --- a/src/xschem.h +++ b/src/xschem.h @@ -668,7 +668,7 @@ typedef struct } Iterator_ctx; -/* will be used some day? <<<< */ +/* context struct for waveform graphs */ typedef struct { double digital; double rx1, ry1, rx2, ry2, rw, rh; /* container rectangle */ @@ -905,13 +905,15 @@ typedef struct { int hide_symbols; int netlist_type; char * top_path; - /* top_path is the path prefix of drawing canvas: + /* top_path is the path prefix of drawing canvas (current_win_path): * - * canvas top_path - * ---------------------- - * ".drw" "" - * ".xx.drw" ".xx" + * current_win_path + * canvas top_path + * ---------------------------- + * ".drw" "" + * ".xx.drw" ".xx" */ + char *current_win_path; int *fill_type; /* for every layer: 0: no fill, 1, solid fill, 2: stipple fill */ int fill_pattern; int draw_window; @@ -1368,7 +1370,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 *filename); -extern void new_schematic(const char *what, const char *top_path, const char *tk_win_path, const char *filename); +extern int new_schematic(const char *what, const char *win_path, const char *filename); extern int window_state (Display *disp, Window win, char *arg); extern void toggle_fullscreen(const char *topwin); extern void toggle_only_probes(); diff --git a/src/xschem.tcl b/src/xschem.tcl index 50681aa8..6d72753a 100644 --- a/src/xschem.tcl +++ b/src/xschem.tcl @@ -3586,6 +3586,7 @@ proc context_menu { } { # # toolbar: Public variables that we allow to be overridden +# Code contributed by Neil Johnson (github: nejohnson) # proc setup_toolbar {} { global toolbar_visible toolbar_horiz toolbar_list XSCHEM_SHAREDIR @@ -3656,10 +3657,14 @@ proc toolbar_create {name cmd { help "" } {topwin {} } } { # adding any separators as needed. # proc toolbar_show { { topwin {} } } { - global toolbar_horiz toolbar_list toolbar_visible + global toolbar_horiz toolbar_list toolbar_visible tabbed_interface if { ! $toolbar_visible } { return } if { $toolbar_horiz } { - pack $topwin.toolbar -fill x -before $topwin.drw + if {$tabbed_interface} { + pack $topwin.toolbar -fill x -before $topwin.tabs + } else { + pack $topwin.toolbar -fill x -before $topwin.drw + } } else { pack $topwin.toolbar -side left -anchor w -fill y -before $topwin.drw } @@ -3709,6 +3714,22 @@ proc toolbar_hide { { topwin {} } } { set $toolbar_visible 0 } + +proc set_tab_names {} { + global tabbed_interface has_x + + if {[info exists has_x] && $tabbed_interface } { + set currwin [xschem get current_win_path] + # puts "set_tab_names : currwin=$currwin" + if { $currwin eq {.drw}} { + .tabs.x0 configure -text [file rootname [file tail [xschem get schname]]] + } else { + regsub {\.drw} $currwin {} top_path + .tabs$top_path configure -text [file rootname [file tail [xschem get schname]]] + } + } +} + proc raise_dialog {parent window_path } { if {[winfo exists .dialog] && [winfo ismapped .dialog] && [winfo ismapped $parent] && [wm stackorder .dialog isbelow $parent ]} { @@ -3735,91 +3756,10 @@ proc every {interval script} { after $interval [list every $interval $script] } -proc new_window {what {filename {}} {path {-}}} { - global max_new_windows - if { $what eq {create}} { - if {$tctx::cnt == 0} { - save_ctx .drw - } - if {$tctx::cnt + 1 >= $max_new_windows} { - puts "proc new_window: no more free slots" - return - } - incr tctx::cnt - - if {$path eq {-}} { - for {set i 1} {$i <= $tctx::cnt} {incr i} { - if {![winfo exists .x$i]} { - set path .x$i - break - } - } - } - toplevel $path -bg {} -width 400 -height 400 - build_widgets $path - pack_widgets $path - update - xschem new_schematic create $path $path.drw [abs_sym_path $filename] - set_bindings $path.drw - # set bindings after creating new schematic otherwise - # a Configure or Expose event is sent before window setup completed. - save_ctx $path.drw - xschem windowid $path ;# set icon for window - return $path - } elseif { $what eq {destroy}} { - set path $filename - xschem new_schematic switch $path $path.drw {} - #### no need to switch tcl context to something that is about to be deleted - # restore_ctx $path.drw - # housekeeping_ctx - #### - # xschem new_schematic destroy also decrements tctx::cnt - xschem new_schematic destroy $path $path.drw {} - #### following 3 lines already done in new_schematic("destroy",...) - # restore_ctx .drw - # housekeeping_ctx - # xschem new_schematic switch . .drw {} - #### - } elseif { $what eq {destroy_all}} { - set all_closed 1 - foreach i [info globals .*.drw] { - regsub {\.drw$} $i {} j - xschem new_schematic switch $j $i {} - #### no need to switch tcl context to something that is about to be deleted - # restore_ctx $i - # housekeeping_ctx - #### - xschem new_schematic destroy $j $i {} - #### following 3 lines already done in new_schematic("destroy",...) - # restore_ctx .drw - # housekeeping_ctx - # xschem new_schematic switch . .drw {} - #### - if { [winfo exists $i] } { set all_closed 0} - } - return $all_closed - } - return {} -} - -#### TEST MODE ##### -proc test1 {} { - xschem load [abs_sym_path rom8k.sch] - new_window create [abs_sym_path mos_power_ampli.sch] .xx - new_window create [abs_sym_path solar_panel.sch] .yy -} - -#### TEST MODE ##### -proc test1_end {} { - new_window destroy .xx - new_window destroy .yy -} - ## tcl context switching global namespace namespace eval tctx { variable tctx variable i - variable cnt 0 variable global_list variable global_array_list variable dialog_list @@ -3845,6 +3785,7 @@ proc no_open_dialogs {} { ## "textwindow_wcounter" should be kept unique as it is the number of open textwindows ## "viewdata_wcounter" should be kept unique as it is the number of open viewdatas ## "measure_id" should be kept unique since we allow only one measure tooltip in graphs +## tabbed_interface is unique set tctx::global_list { auto_hilight autotrim_wires bespice_listen_port big_grid_points bus_replacement_char @@ -3892,6 +3833,7 @@ proc delete_ctx {context} { } proc restore_ctx {context} { + # puts "restoring tcl context $context : semaphore=[xschem get semaphore]" set tctx::tctx $context array unset ::sim uplevel #0 { @@ -3914,6 +3856,7 @@ proc restore_ctx {context} { } proc save_ctx {context} { + # puts "saving tcl context $context : semaphore=[xschem get semaphore]" set tctx::tctx $context uplevel #0 { # puts "save_ctx $tctx::tctx" @@ -4007,7 +3950,7 @@ global env has_x OS ## this could lead to crashes on some (may be slow) systems due to Configure/Expose events being delivered ## before xschem being ready to handle them. proc pack_widgets { { topwin {} } } { - global env has_x OS + global env has_x OS tabbed_interface if {($OS== "Windows" || [string length [lindex [array get env DISPLAY] 1] ] > 0 ) && [info exists has_x]} { pack $topwin.statusbar.2 -side left pack $topwin.statusbar.3 -side left @@ -4017,8 +3960,11 @@ proc pack_widgets { { topwin {} } } { pack $topwin.statusbar.7 -side left pack $topwin.statusbar.8 -side left pack $topwin.statusbar.1 -side left -fill x + pack $topwin.menubar -anchor n -side top -fill x + if {$tabbed_interface} { + pack $topwin.tabs -fill x -side top -expand false -side top + } pack $topwin.drw -anchor n -side top -fill both -expand true - pack $topwin.menubar -anchor n -side top -fill x -before $topwin.drw toolbar_show $topwin pack $topwin.statusbar -after $topwin.drw -anchor sw -fill x bind $topwin.statusbar.5 "set cadgrid \[$topwin.statusbar.5 get\]; xschem set cadgrid \$cadgrid" @@ -4054,7 +4000,7 @@ proc switch_undo {} { } proc build_widgets { {topwin {} } } { - global XSCHEM_SHAREDIR + global XSCHEM_SHAREDIR tabbed_interface global colors recentfile color_ps transparent_svg menu_debug_var enable_stretch global netlist_show flat_netlist split_files hspice_netlist tmp_bus_char global draw_grid big_grid_points sym_txt change_lw incr_hilight symbol_width @@ -4067,6 +4013,15 @@ proc build_widgets { {topwin {} } } { set mbg {-bg gray50} set bbg {-bg gray50 -highlightthickness 0} } + + if { $tabbed_interface } { + frame $topwin.tabs + button $topwin.tabs.x0 -padx 2 -pady 0 -text Main -command {xschem new_schematic switch .drw} + button $topwin.tabs.x1 -padx 2 -pady 0 -text Tab1 -command {xschem new_schematic switch .x1.drw} + button $topwin.tabs.x2 -padx 2 -pady 0 -text Tab2 -command {xschem new_schematic switch .x2.drw} + button $topwin.tabs.add -padx 0 -pady 0 -text { + } + pack $topwin.tabs.x0 $topwin.tabs.x1 $topwin.tabs.x2 $topwin.tabs.add -side left + } eval frame $topwin.menubar -relief raised -bd 2 $mbg toolbar_toolbar $topwin eval menubutton $topwin.menubar.file -text "File" -menu $topwin.menubar.file.menu \ @@ -4134,6 +4089,10 @@ proc build_widgets { {topwin {} } } { setup_recent_menu 0 $topwin setup_recent_menu 1 $topwin $topwin.menubar.file.menu add command -label {Open new window [exp]} -command "xschem load_new_window" + if {$tabbed_interface} { + $topwin.menubar.file.menu entryconfigure 6 -state disabled + $topwin.menubar.file.menu entryconfigure 7 -state disabled + } toolbar_create FileOpen "xschem load" "Open File" $topwin $topwin.menubar.file.menu add command -label "Delete files" -command "xschem delete_files" -accelerator {Shift-D} @@ -4166,10 +4125,10 @@ proc build_widgets { {topwin {} } } { $topwin.menubar.file.menu add command -label "SVG Export" -command "xschem print svg" -accelerator {Alt+*} $topwin.menubar.file.menu add separator $topwin.menubar.file.menu add command -label "Exit" -accelerator {Ctrl+Q} -command " - if { {$topwin} eq {} } { + if { \[xschem get current_win_path\] eq {.drw} } { xschem exit } else { - xschem new_schematic destroy $topwin $topwin.drw {} + xschem new_schematic destroy \[xschem get current_win_path\] {} }" $topwin.menubar.option.menu add checkbutton -label "Color Postscript/SVG" -variable color_ps \ -command { @@ -4537,7 +4496,7 @@ proc build_widgets { {topwin {} } } { if { $rootwin == {.}} { wm protocol $rootwin WM_DELETE_WINDOW {xschem exit} } else { - wm protocol $topwin WM_DELETE_WINDOW "xschem new_schematic destroy $topwin $topwin.drw {}" + wm protocol $topwin WM_DELETE_WINDOW "xschem new_schematic destroy $topwin.drw {}" } frame $topwin.statusbar @@ -4799,6 +4758,9 @@ set_ne to_pdf {ps2pdf} ## undo_type: disk or memory set_ne undo_type disk +## show tab bar (tabbed interface) +set_ne tabbed_interface 0 + ## max number of windows (including main) a single xschem process can handle set_ne max_new_windows -1 ;# this is set by xinit.c ## remember edit_prop widget size