xschem/src/actions.c

3508 lines
124 KiB
C

/* File: actions.c
*
* This file is part of XSCHEM,
* a schematic capture and Spice/Vhdl/Verilog netlisting tool for circuit
* simulation.
* Copyright (C) 1998-2023 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"
#ifdef __unix__
#include <sys/wait.h> /* waitpid */
#endif
void here(double i)
{
dbg(0, "here %g\n", i);
}
/* super simple 32 bit hashing function for files
* It is suppoded to be used on text files.
* Calculates the same hash on windows (crlf) and unix (lf) text files.
* If you want high collision resistance and
* avoid 'birthday problem' collisions use a better hash function, like md5sum
* or sha256sum
*/
unsigned int hash_file(const char *f, int skip_path_lines)
{
FILE *fd;
int i;
size_t n;
int cr = 0;
unsigned int h=5381;
char *line = NULL;
fd = fopen(f, "r"); /* windows won't return \r in the lines and we chop them out anyway in the code */
if(fd) {
while((line = my_fgets(fd, &n))) {
/* skip lines of type: '** sch_path: ...' or '-- sch_path: ...' or '// sym_path: ...' */
if(skip_path_lines && n > 14) {
if(!strncmp(line+2, " sch_path: ", 11) || !strncmp(line+2, " sym_path: ", 11) ) {
my_free(_ALLOC_ID_, &line);
continue;
}
}
for(i = 0; i < n; ++i) {
/* skip CRs so hashes will match on unix / windows */
if(line[i] == '\r') {
cr = 1;
continue;
} else if(line[i] == '\n' && cr) {
cr = 0;
} else if(cr) { /* no skip \r if not followed by \n */
cr = 0;
h += (h << 5) + '\r';
}
h += (h << 5) + (unsigned char)line[i];
}
my_free(_ALLOC_ID_, &line);
} /* while(line ....) */
if(cr) h += (h << 5) + '\r'; /* file ends with \r not followed by \n: keep it */
fclose(fd);
return h;
} else {
dbg(0, "Can not open file %s\n", f);
}
return 0;
}
int there_are_floaters(void)
{
int floaters = 0, k;
for(k = 0; k < xctx->texts; k++) {
if(xctx->text[k].flags & TEXT_FLOATER) {
floaters = 1;
dbg(1, "text %d is a floater\n", k);
break;
}
}
return floaters;
}
const char *get_text_floater(int i)
{
const char *txt_ptr = xctx->text[i].txt_ptr;
if(xctx->text[i].flags & TEXT_FLOATER) {
int inst = -1;
const char *instname;
if(!xctx->floater_inst_table.table) {
floater_hash_all_names();
}
if(xctx->text[i].floater_instname)
instname = xctx->text[i].floater_instname;
else {
instname = get_tok_value(xctx->text[i].prop_ptr, "name", 0);
if(!xctx->tok_size) {
instname = get_tok_value(xctx->text[i].prop_ptr, "floater", 0);
}
}
inst = get_instance(instname);
if(inst >= 0) {
if(xctx->text[i].floater_ptr) {
txt_ptr = xctx->text[i].floater_ptr;
} else {
/* cache floater translated text to avoid re-evaluating every time schematic is drawn */
my_strdup2(_ALLOC_ID_, &xctx->text[i].floater_ptr, translate(inst, xctx->text[i].txt_ptr));
txt_ptr = xctx->text[i].floater_ptr;
}
dbg(1, "floater: %s\n",txt_ptr);
} else {
/* do just a tcl substitution if floater does not reference an existing instance
* (but name=something attribute must be present) and text matches tcleval(...) */
if(strstr(txt_ptr, "tcleval(") == txt_ptr) {
my_strdup2(_ALLOC_ID_, &xctx->text[i].floater_ptr, tcl_hook2(xctx->text[i].txt_ptr));
txt_ptr = xctx->text[i].floater_ptr;
}
}
}
return txt_ptr;
}
/* mod=-1 used to force set title
* mod=-2 used to reset floaters cache
* if floaters are present set_modify(1) (after a modify opration) must be done before draw()
* to invalidate cached floater string values before redrawing
* return 1 if floaters are found (mod==-2 or mod == 1 or mod == -1) */
int set_modify(int mod)
{
int i, floaters = 0;
if(mod != -2 && mod != -1) xctx->modified = mod;
dbg(1, "set_modify(): %d\n", mod);
if(mod == 1 || mod == -1 || mod == -2) {
/* hash instance names if there are (many) floaters and many instances for faster lookup */
for(i = 0; i < xctx->texts; i++)
if(xctx->text[i].flags & TEXT_FLOATER) {
floaters++;
my_free(_ALLOC_ID_, &xctx->text[i].floater_ptr); /* clear floater cached value */
}
int_hash_free(&xctx->floater_inst_table);
}
if(mod != -2 && (mod == -1 || mod != xctx->prev_set_modify) ) { /* mod=-1 used to force set title */
if(mod != -1) xctx->prev_set_modify = mod;
else mod = xctx->modified;
if(has_x && strcmp(get_cell(xctx->sch[xctx->currsch],1), "systemlib/font")) {
char *top_path = xctx->top_path[0] ? xctx->top_path : ".";
if(mod == 1) {
tclvareval("wm title ", top_path, " \"xschem - [file tail [xschem get schname]]*\"", NULL);
tclvareval("wm iconname ", top_path, " \"xschem - [file tail [xschem get schname]]*\"", NULL);
} else {
tclvareval("wm title ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL);
tclvareval("wm iconname ", top_path, " \"xschem - [file tail [xschem get schname]]\"", NULL);
}
}
if(xctx->modified) tcleval("set_tab_names *");
else tcleval("set_tab_names");
}
return floaters;
}
void print_version()
{
printf("XSCHEM V%s\n", XSCHEM_VERSION);
printf("Copyright (C) 1998-2023 Stefan Schippers\n");
printf("\n");
printf("This is free software; see the source for copying conditions. There is NO\n");
printf("warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n");
}
char *escape_chars(const char *source)
{
int s=0;
int d=0;
static char *dest = NULL;
size_t slen, size;
if(!source) {
if(dest) my_free(_ALLOC_ID_, &dest);
return NULL;
}
slen = strlen(source);
size = slen + 1;
my_realloc(_ALLOC_ID_, &dest, size);
while(source && source[s]) {
if(d >= size - 2) {
size += 2;
my_realloc(_ALLOC_ID_, &dest, size);
}
switch(source[s]) {
case '\n':
dest[d++] = '\\';
dest[d++] = 'n';
break;
case '\t':
dest[d++] = '\\';
dest[d++] = 't';
break;
case '\\':
case '\'':
case ' ':
case ';':
case '$':
case '!':
case '#':
case '{':
case '}':
case '[':
case ']':
case '"':
dest[d++] = '\\';
dest[d++] = source[s];
break;
default:
dest[d++] = source[s];
}
s++;
}
dest[d] = '\0';
return dest;
}
void set_snap(double newsnap) /* 20161212 set new snap factor and just notify new value */
{
static double default_snap = -1.0; /* safe to keep even with multiple schematics, set at program start */
double cs;
cs = tclgetdoublevar("cadsnap");
if(default_snap == -1.0) {
default_snap = cs;
if(default_snap==0.0) default_snap = CADSNAP;
}
cs = newsnap ? newsnap : default_snap;
if(has_x) {
if(cs == default_snap) {
tclvareval(xctx->top_path, ".statusbar.3 configure -background PaleGreen", NULL);
} else {
tclvareval(xctx->top_path, ".statusbar.3 configure -background OrangeRed", NULL);
}
}
tclsetdoublevar("cadsnap", cs);
}
void set_grid(double newgrid)
{
static double default_grid = -1.0; /* safe to keep even with multiple schematics, set at program start */
double cg;
cg = tclgetdoublevar("cadgrid");
if(default_grid == -1.0) {
default_grid = cg;
if(default_grid==0.0) default_grid = CADGRID;
}
cg = newgrid ? newgrid : default_grid;
dbg(1, "set_grid(): default_grid = %.16g, cadgrid=%.16g\n", default_grid, cg);
if(has_x) {
if(cg == default_grid) {
tclvareval(xctx->top_path, ".statusbar.5 configure -background PaleGreen", NULL);
} else {
tclvareval(xctx->top_path, ".statusbar.5 configure -background OrangeRed", NULL);
}
}
tclsetdoublevar("cadgrid", cg);
}
int set_netlist_dir(int force, const char *dir)
{
char cmd[PATH_MAX+200];
if(dir) my_snprintf(cmd, S(cmd), "set_netlist_dir %d {%s}", force, dir);
else my_snprintf(cmd, S(cmd), "set_netlist_dir %d", force);
tcleval(cmd);
if(!strcmp("", tclresult()) ) {
return 0;
}
return 1;
}
/* wrapper to TCL function */
/* remove parameter section of symbol generator before calculating abs path : xxx(a,b) -> xxx */
const char *sanitized_abs_sym_path(const char *s, const char *ext)
{
char c[PATH_MAX+1000];
my_snprintf(c, S(c), "abs_sym_path [regsub {\\(.*} {%s} {}] {%s}", s, ext);
tcleval(c);
return tclresult();
}
/* wrapper to TCL function */
const char *abs_sym_path(const char *s, const char *ext)
{
char c[PATH_MAX+1000];
my_snprintf(c, S(c), "abs_sym_path {%s} {%s}", s, ext);
tcleval(c);
return tclresult();
}
/* Wrapper to Tcl function */
const char *rel_sym_path(const char *s)
{
char c[PATH_MAX+1000];
my_snprintf(c, S(c), "rel_sym_path {%s}", s);
tcleval(c);
return tclresult();
}
const char *add_ext(const char *f, const char *ext)
{
static char ff[PATH_MAX]; /* safe to keep even with multiple schematics */
char *p;
int i;
dbg(1, "add_ext(): f=%s ext=%s\n", f, ext);
if(strchr(f,'(')) my_strncpy(ff, f, S(ff)); /* generator: return as is */
else {
if((p=strrchr(f,'.'))) {
my_strncpy(ff, f, (p-f) + 1);
p = ff + (p-f);
dbg(1, "add_ext(): 1: ff=%s\n", ff);
} else {
i = my_strncpy(ff, f, S(ff));
p = ff+i;
dbg(1, "add_ext(): 2: ff=%s\n", ff);
}
my_strncpy(p, ext, S(ff)-(p-ff));
dbg(1, "add_ext(): 3: ff=%s\n", ff);
}
return ff;
}
void toggle_only_probes()
{
xctx->only_probes = tclgetboolvar("only_probes");
draw();
}
#ifdef __unix__
void new_xschem_process(const char *cell, int symbol)
{
char f[PATH_MAX]; /* overflow safe 20161122 */
struct stat buf;
pid_t pid1;
pid_t pid2;
int status;
dbg(1, "new_xschem_process(): executable: %s, cell=%s, symbol=%d\n", xschem_executable, cell, symbol);
if(stat(xschem_executable,&buf)) {
fprintf(errfp, "new_xschem_process(): executable not found\n");
return;
}
fflush(NULL); /* flush all stdio streams before process forking */
/* double fork method to avoid zombies 20180925*/
if ( (pid1 = fork()) > 0 ) {
/* parent process */
waitpid(pid1, &status, 0);
} else if (pid1 == 0) {
/* child process */
if ( (pid2 = fork()) > 0 ) {
_exit(0); /* --> child of child will be reparented to init */
} else if (pid2 == 0) {
/* child of child */
if(!cell || !cell[0]) {
if(!symbol)
execl(xschem_executable,xschem_executable, "-b", "-s", "--tcl",
"set XSCHEM_START_WINDOW {}", NULL);
else
execl(xschem_executable,xschem_executable, "-b", "-y", "--tcl",
"set XSCHEM_START_WINDOW {}", NULL);
}
else if(!symbol) {
my_strncpy(f, cell, S(f));
execl(xschem_executable,xschem_executable, "-b", "-s", f, NULL);
}
else {
my_strncpy(f, cell, S(f));
execl(xschem_executable,xschem_executable, "-b", "-y", f, NULL);
}
} else {
/* error */
fprintf(errfp, "new_xschem_process(): fork error 1\n");
_exit(1);
}
} else {
/* error */
fprintf(errfp, "new_xschem_process(): fork error 2\n");
tcleval("exit");
}
}
#else
void new_xschem_process(const char* cell, int symbol)
{
char cmd_line[2 * PATH_MAX + 100];
struct stat buf;
dbg(1, "new_xschem_process(): executable: %s, cell=%s, symbol=%d\n", xschem_executable, cell, symbol);
if (stat(xschem_executable, &buf)) {
fprintf(errfp, "new_xschem_process(): executable not found\n");
return;
}
/* According to Stackoverflow, system should be avoided because it's resource heavy
* and not secure.
* Furthermore, system doesn't spawn a TCL shell with XSchem
*/
/* int result = system(xschem_executable); */
STARTUPINFOA si;
PROCESS_INFORMATION pi;
ZeroMemory(&si, sizeof(si));
si.cb = sizeof(si);
ZeroMemory(&pi, sizeof(pi));
/* "detach" (-b) is not processed for Windows, so
use DETACHED_PROCESS in CreateProcessA to not create
a TCL shell
*/
if (!cell || !cell[0]) {
if (!symbol)
my_snprintf(cmd_line, S(cmd_line), "%s -b -s --tcl \"set XSCHEM_START_WINDOW {}\"", xschem_executable);
else
my_snprintf(cmd_line, S(cmd_line), "%s -b -y --tcl \"set XSCHEM_START_WINDOW {}\"", xschem_executable);
}
else if (!symbol) {
my_snprintf(cmd_line, S(cmd_line), "%s -b -s \"%s\"", xschem_executable, cell);
}
else {
my_snprintf(cmd_line, S(cmd_line), "%s -b -y \"%s\"", xschem_executable, cell);
}
CreateProcessA
(
NULL, /* the path */
cmd_line, /* Command line */
NULL, /* Process handle not inheritable */
NULL, /* Thread handle not inheritable */
FALSE, /* Set handle inheritance to FALSE */
DETACHED_PROCESS, /* Opens file in a separate console */
NULL, /* Use parent's environment block */
NULL, /* Use parent's starting directory */
&si, /* Pointer to STARTUPINFO structure */
&pi /* Pointer to PROCESS_INFORMATION structure */
);
}
#endif
const char *get_file_path(char *f)
{
char tmp[2*PATH_MAX+100];
my_snprintf(tmp, S(tmp),"get_file_path {%s}", f);
tcleval(tmp);
return tclresult();
}
/* return value:
* 1 : file saved or not needed to save since no change
* -1 : user cancel
* 0 : file not saved due to errors or per user request
*/
int save(int confirm)
{
struct stat buf;
char *name = xctx->sch[xctx->currsch];
int force = 0;
if(!stat(name, &buf)) {
if(xctx->time_last_modify && xctx->time_last_modify != buf.st_mtime) {
force = 1;
confirm = 0;
}
}
if(force || xctx->modified)
{
if(confirm) {
tcleval("ask_save_optional");
if(!strcmp(tclresult(), "") ) return -1; /* user clicks "Cancel" */
else if(!strcmp(tclresult(), "yes") ) return save_schematic(xctx->sch[xctx->currsch]);
else return 0; /* user clicks "no" */
} else {
return save_schematic(xctx->sch[xctx->currsch]);
}
}
return 1; /* circuit not changed: always succeeed */
}
void saveas(const char *f, int type) /* changed name from ask_save_file to saveas 20121201 */
{
char name[PATH_MAX+1000];
char filename[PATH_MAX];
char res[PATH_MAX];
char *p;
if(!f && has_x) {
my_strncpy(filename , xctx->sch[xctx->currsch], S(filename));
if(type == SYMBOL) {
if( (p = strrchr(filename, '.')) && !strcmp(p, ".sch") ) {
my_strncpy(filename, add_ext(filename, ".sym"), S(filename));
}
my_snprintf(name, S(name), "save_file_dialog {Save file} *.\\{sch,sym\\} INITIALLOADDIR {%s}", filename);
} else {
my_snprintf(name, S(name), "save_file_dialog {Save file} *.\\{sch,sym\\} INITIALLOADDIR {%s}", filename);
}
tcleval(name);
my_strncpy(res, tclresult(), S(res));
}
else if(f) {
my_strncpy(res, f, S(res));
}
else res[0]='\0';
if(!res[0]) return;
dbg(1, "saveas(): res = %s\n", res);
save_schematic(res);
tclvareval("update_recent_file {", res,"}", NULL);
return;
}
void ask_new_file(void)
{
char f[PATH_MAX]; /* overflow safe 20161125 */
if(!has_x) return;
if(xctx->modified) {
if(save(1) == -1 ) return; /* user cancels save, so do nothing. */
}
tcleval("load_file_dialog {Load file} *.\\{sch,sym\\} INITIALLOADDIR");
my_snprintf(f, S(f),"%s", tclresult());
if(f[0]) {
char win_path[WINDOW_PATH_SIZE];
int skip = 0;
dbg(1, "ask_new_file(): load: 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")) skip = 1;
}
if(!skip) {
dbg(1, "ask_new_file(): load file: %s\n", f);
clear_all_hilights();
xctx->currsch = 0;
unselect_all(1);
remove_symbols();
xctx->zoom=CADINITIALZOOM;
xctx->mooz=1/CADINITIALZOOM;
xctx->xorigin=CADINITIALX;
xctx->yorigin=CADINITIALY;
load_schematic(1, f, 1, 1);
tclvareval("update_recent_file {", f, "}", NULL);
if(xctx->portmap[xctx->currsch].table) str_hash_free(&xctx->portmap[xctx->currsch]);
my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch],".");
xctx->sch_path_hash[xctx->currsch] = 0;
xctx->sch_inst_number[xctx->currsch] = 1;
zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97);
}
}
}
/* remove symbol and decrement symbols */
/* Warning: removing a symbol with a loaded schematic will make all symbol references corrupt */
/* you should clear_drawing() first or load_schematic() or link_symbols_to_instances()
immediately afterwards */
void remove_symbol(int j)
{
int i,c;
xSymbol save;
dbg(1,"clearing symbol %d: %s\n", j, xctx->sym[j].name);
my_free(_ALLOC_ID_, &xctx->sym[j].prop_ptr);
my_free(_ALLOC_ID_, &xctx->sym[j].templ);
my_free(_ALLOC_ID_, &xctx->sym[j].type);
my_free(_ALLOC_ID_, &xctx->sym[j].name);
/* /20150409 */
for(c=0;c<cadlayers; ++c) {
for(i=0;i<xctx->sym[j].polygons[c]; ++i) {
if(xctx->sym[j].poly[c][i].prop_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].poly[c][i].prop_ptr);
}
my_free(_ALLOC_ID_, &xctx->sym[j].poly[c][i].x);
my_free(_ALLOC_ID_, &xctx->sym[j].poly[c][i].y);
my_free(_ALLOC_ID_, &xctx->sym[j].poly[c][i].selected_point);
}
my_free(_ALLOC_ID_, &xctx->sym[j].poly[c]);
xctx->sym[j].polygons[c] = 0;
for(i=0;i<xctx->sym[j].lines[c]; ++i) {
if(xctx->sym[j].line[c][i].prop_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].line[c][i].prop_ptr);
}
}
my_free(_ALLOC_ID_, &xctx->sym[j].line[c]);
xctx->sym[j].lines[c] = 0;
for(i=0;i<xctx->sym[j].arcs[c]; ++i) {
if(xctx->sym[j].arc[c][i].prop_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].arc[c][i].prop_ptr);
}
}
my_free(_ALLOC_ID_, &xctx->sym[j].arc[c]);
xctx->sym[j].arcs[c] = 0;
for(i=0;i<xctx->sym[j].rects[c]; ++i) {
if(xctx->sym[j].rect[c][i].prop_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].rect[c][i].prop_ptr);
}
set_rect_extraptr(0, &xctx->sym[j].rect[c][i]);
}
my_free(_ALLOC_ID_, &xctx->sym[j].rect[c]);
xctx->sym[j].rects[c] = 0;
}
for(i=0;i<xctx->sym[j].texts; ++i) {
if(xctx->sym[j].text[i].prop_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].text[i].prop_ptr);
}
if(xctx->sym[j].text[i].txt_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].text[i].txt_ptr);
dbg(1, "remove_symbol(): freeing symbol %d text_ptr %d\n", j, i);
}
if(xctx->sym[j].text[i].font != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].text[i].font);
}
if(xctx->sym[j].text[i].floater_instname != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].text[i].floater_instname);
}
if(xctx->sym[j].text[i].floater_ptr != NULL) {
my_free(_ALLOC_ID_, &xctx->sym[j].text[i].floater_ptr);
}
}
my_free(_ALLOC_ID_, &xctx->sym[j].text);
my_free(_ALLOC_ID_, &xctx->sym[j].line);
my_free(_ALLOC_ID_, &xctx->sym[j].rect);
my_free(_ALLOC_ID_, &xctx->sym[j].arc);
my_free(_ALLOC_ID_, &xctx->sym[j].poly);
my_free(_ALLOC_ID_, &xctx->sym[j].lines);
my_free(_ALLOC_ID_, &xctx->sym[j].polygons);
my_free(_ALLOC_ID_, &xctx->sym[j].arcs);
my_free(_ALLOC_ID_, &xctx->sym[j].rects);
xctx->sym[j].texts = 0;
save = xctx->sym[j]; /* save cleared symbol slot */
for(i = j + 1; i < xctx->symbols; ++i) {
xctx->sym[i-1] = xctx->sym[i];
}
xctx->sym[xctx->symbols-1] = save; /* fill end with cleared symbol slot */
xctx->symbols--;
}
void remove_symbols(void)
{
int j;
for(j = 0; j < xctx->instances; ++j) {
delete_inst_node(j); /* must be deleted before symbols are deleted */
xctx->inst[j].ptr = -1; /* clear symbol reference on instanecs */
}
for(j=xctx->symbols-1;j>=0;j--) {
dbg(2, "remove_symbols(): removing symbol %d\n",j);
remove_symbol(j);
}
dbg(1, "remove_symbols(): done\n");
}
/* set cached rect .flags bitmask based on attributes, currently:
* graph 1
* graph_unlocked 1 + 2
* image 1024
* image_unscaled 1024 + 2048
*/
int set_rect_flags(xRect *r)
{
const char *flags;
unsigned short f = 0;
if(r->prop_ptr && r->prop_ptr[0]) {
flags = get_tok_value(r->prop_ptr,"flags",0);
if(strstr(flags, "unscaled")) f |= 3072;
else if(strstr(flags, "image")) f |= 1024;
else if(strstr(flags, "unlocked")) f |= 3;
else if(strstr(flags, "graph")) f |= 1;
}
r->flags = f;
dbg(1, "set_rect_flags(): flags=%d\n", f);
return f;
}
int set_sym_flags(xSymbol *sym)
{
const char *ptr;
sym->flags = 0;
my_strdup2(_ALLOC_ID_, &sym->templ,
get_tok_value(sym->prop_ptr, "template", 0));
my_strdup2(_ALLOC_ID_, &sym->type,
get_tok_value(sym->prop_ptr, "type",0));
if(!strboolcmp(get_tok_value(sym->prop_ptr,"highlight",0), "true"))
sym->flags |= HILIGHT_CONN;
if(!strboolcmp(get_tok_value(sym->prop_ptr,"hide",0), "true"))
sym->flags |= HIDE_INST;
ptr = get_tok_value(sym->prop_ptr,"spice_ignore",0);
if(!strcmp(ptr, "short"))
sym->flags |= SPICE_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
sym->flags |= SPICE_IGNORE;
ptr = get_tok_value(sym->prop_ptr,"verilog_ignore",0);
if(!strcmp(ptr, "short"))
sym->flags |= VERILOG_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
sym->flags |= VERILOG_IGNORE;
ptr = get_tok_value(sym->prop_ptr,"vhdl_ignore",0);
if(!strcmp(ptr, "short"))
sym->flags |= VHDL_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
sym->flags |= VHDL_IGNORE;
ptr = get_tok_value(sym->prop_ptr,"tedax_ignore",0);
if(!strcmp(ptr, "short"))
sym->flags |= TEDAX_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
sym->flags |= TEDAX_IGNORE;
ptr = get_tok_value(sym->prop_ptr,"lvs_ignore",0);
if(!strcmp(ptr, "short"))
sym->flags |= LVS_IGNORE_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
sym->flags |= LVS_IGNORE_OPEN;
dbg(1, "set_sym_flags: inst %s flags=%d\n", sym->name, sym->flags);
return 0;
}
int set_inst_flags(xInstance *inst)
{
const char *ptr;
inst->flags &= IGNORE_INST; /* do not clear IGNORE_INST bit, used in draw_symbol() */
my_strdup2(_ALLOC_ID_, &inst->instname, get_tok_value(inst->prop_ptr, "name", 0));
dbg(1, "set_inst_flags(): instname=%s\n", inst->instname);
if(inst->ptr >=0) {
char *type = xctx->sym[inst->ptr].type;
int cond= type && IS_LABEL_SH_OR_PIN(type);
if(cond) {
inst->flags |= PIN_OR_LABEL;
my_strdup2(_ALLOC_ID_, &(inst->lab), get_tok_value(inst->prop_ptr,"lab",0));
}
}
if(!strboolcmp(get_tok_value(inst->prop_ptr,"hide",0), "true"))
inst->flags |= HIDE_INST;
ptr = get_tok_value(inst->prop_ptr,"spice_ignore",0);
if(!strcmp(ptr, "short"))
inst->flags |= SPICE_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
inst->flags |= SPICE_IGNORE;
ptr = get_tok_value(inst->prop_ptr,"verilog_ignore",0);
if(!strcmp(ptr, "short"))
inst->flags |= VERILOG_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
inst->flags |= VERILOG_IGNORE;
ptr = get_tok_value(inst->prop_ptr,"vhdl_ignore",0);
if(!strcmp(ptr, "short"))
inst->flags |= VHDL_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
inst->flags |= VHDL_IGNORE;
ptr = get_tok_value(inst->prop_ptr,"tedax_ignore",0);
if(!strcmp(ptr, "short"))
inst->flags |= TEDAX_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
inst->flags |= TEDAX_IGNORE;
ptr = get_tok_value(inst->prop_ptr,"lvs_ignore",0);
if(!strcmp(ptr, "short"))
inst->flags |= LVS_IGNORE_SHORT;
else if(!strboolcmp(ptr, "true") || !strcmp(ptr, "open"))
inst->flags |= LVS_IGNORE_OPEN;
if(!strboolcmp(get_tok_value(inst->prop_ptr,"hide_texts",0), "true"))
inst->flags |= HIDE_SYMBOL_TEXTS;
if(!strboolcmp(get_tok_value(inst->prop_ptr,"highlight",0), "true"))
inst->flags |= HILIGHT_CONN;
inst->embed = !strboolcmp(get_tok_value(inst->prop_ptr, "embed", 2), "true");
dbg(1, "set_inst_flags: inst %s flags=%d\n", inst->instname, inst->flags);
return 0;
}
int set_text_flags(xText *t)
{
const char *str;
t->flags = 0;
t->hcenter = 0;
t->vcenter = 0;
t->layer = -1;
if(t->prop_ptr) {
my_strdup(_ALLOC_ID_, &t->font, get_tok_value(t->prop_ptr, "font", 0));
str = get_tok_value(t->prop_ptr, "hcenter", 0);
t->hcenter = strboolcmp(str, "true") ? 0 : 1;
str = get_tok_value(t->prop_ptr, "vcenter", 0);
t->vcenter = strboolcmp(str, "true") ? 0 : 1;
str = get_tok_value(t->prop_ptr, "layer", 0);
if(str[0]) t->layer = atoi(str);
str = get_tok_value(t->prop_ptr, "slant", 0);
t->flags |= strcmp(str, "oblique") ? 0 : TEXT_OBLIQUE;
t->flags |= strcmp(str, "italic") ? 0 : TEXT_ITALIC;
str = get_tok_value(t->prop_ptr, "weight", 0);
t->flags |= strcmp(str, "bold") ? 0 : TEXT_BOLD;
str = get_tok_value(t->prop_ptr, "hide", 0);
t->flags |= strboolcmp(str, "true") ? 0 : HIDE_TEXT;
str = get_tok_value(t->prop_ptr, "name", 0);
if(!xctx->tok_size) str = get_tok_value(t->prop_ptr, "floater", 0);
t->flags |= xctx->tok_size ? TEXT_FLOATER : 0;
my_strdup2(_ALLOC_ID_, &t->floater_instname, str);
}
return 0;
}
void reset_flags(void)
{
int i;
dbg(1, "reset_flags()\n");
for(i = 0; i < xctx->instances; i++) {
set_inst_flags(&xctx->inst[i]);
}
for(i = 0; i < xctx->symbols; i++) {
set_sym_flags(&xctx->sym[i]);
}
}
/* what:
* 1: create
* 0: clear
*/
int set_rect_extraptr(int what, xRect *drptr)
{
#if HAS_CAIRO==1
if(what==1) { /* create */
if(drptr->flags & 1024) { /* embedded image */
if(!drptr->extraptr) {
xEmb_image *d;
d = my_malloc(_ALLOC_ID_, sizeof(xEmb_image));
d->image = NULL;
drptr->extraptr = d;
}
}
} else { /* clear */
if(drptr->flags & 1024) { /* embedded image */
if(drptr->extraptr) {
xEmb_image *d = drptr->extraptr;
if(d->image) cairo_surface_destroy(d->image);
my_free(_ALLOC_ID_, &drptr->extraptr);
}
}
}
#endif
return 0;
}
void clear_drawing(void)
{
int i,j;
xctx->graph_lastsel = -1;
del_inst_table();
del_wire_table();
my_free(_ALLOC_ID_, &xctx->schtedaxprop);
my_free(_ALLOC_ID_, &xctx->schsymbolprop);
my_free(_ALLOC_ID_, &xctx->schprop);
my_free(_ALLOC_ID_, &xctx->schvhdlprop);
my_free(_ALLOC_ID_, &xctx->version_string);
if(xctx->header_text) my_free(_ALLOC_ID_, &xctx->header_text);
my_free(_ALLOC_ID_, &xctx->schverilogprop);
for(i=0;i<xctx->wires; ++i)
{
my_free(_ALLOC_ID_, &xctx->wire[i].prop_ptr);
my_free(_ALLOC_ID_, &xctx->wire[i].node);
}
xctx->wires = 0;
for(i=0;i<xctx->instances; ++i)
{
my_free(_ALLOC_ID_, &xctx->inst[i].prop_ptr);
my_free(_ALLOC_ID_, &xctx->inst[i].name);
my_free(_ALLOC_ID_, &xctx->inst[i].instname);
my_free(_ALLOC_ID_, &xctx->inst[i].lab);
delete_inst_node(i);
}
xctx->instances = 0;
for(i=0;i<xctx->texts; ++i)
{
my_free(_ALLOC_ID_, &xctx->text[i].font);
my_free(_ALLOC_ID_, &xctx->text[i].floater_instname);
my_free(_ALLOC_ID_, &xctx->text[i].floater_ptr);
my_free(_ALLOC_ID_, &xctx->text[i].prop_ptr);
my_free(_ALLOC_ID_, &xctx->text[i].txt_ptr);
}
xctx->texts = 0;
for(i=0;i<cadlayers; ++i)
{
for(j=0;j<xctx->lines[i]; ++j)
{
my_free(_ALLOC_ID_, &xctx->line[i][j].prop_ptr);
}
for(j=0;j<xctx->rects[i]; ++j)
{
my_free(_ALLOC_ID_, &xctx->rect[i][j].prop_ptr);
set_rect_extraptr(0, &xctx->rect[i][j]);
}
for(j=0;j<xctx->arcs[i]; ++j)
{
my_free(_ALLOC_ID_, &xctx->arc[i][j].prop_ptr);
}
for(j=0;j<xctx->polygons[i]; ++j) {
my_free(_ALLOC_ID_, &xctx->poly[i][j].x);
my_free(_ALLOC_ID_, &xctx->poly[i][j].y);
my_free(_ALLOC_ID_, &xctx->poly[i][j].prop_ptr);
my_free(_ALLOC_ID_, &xctx->poly[i][j].selected_point);
}
xctx->lines[i] = 0;
xctx->arcs[i] = 0;
xctx->rects[i] = 0;
xctx->polygons[i] = 0;
}
dbg(1, "clear drawing(): deleted data structures, now deleting hash\n");
int_hash_free(&xctx->inst_name_table);
int_hash_free(&xctx->floater_inst_table);
}
/* xctx->n_active_layers is the total number of layers for hilights. */
void enable_layers(void)
{
int i;
char tmp[50];
int en;
xctx->n_active_layers = 0;
for(i = 0; i< cadlayers; ++i) {
my_snprintf(tmp, S(tmp), "enable_layer(%d)",i);
en = tclgetboolvar(tmp);
if(!en) xctx->enable_layer[i] = 0;
else {
xctx->enable_layer[i] = 1;
if(i>=7) {
xctx->active_layer[xctx->n_active_layers] = i;
xctx->n_active_layers++;
}
}
}
}
/* not used... */
void clear_partial_selected_wires(void)
{
int j;
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);
}
xctx->need_reb_sel_arr = 1;
rebuild_selected_array();
}
/* Add wires when moving instances or wires */
int connect_by_kissing(void)
{
xSymbol *symbol;
int npin, i, j;
double x0,y0, pinx0, piny0;
int kissing, changed = 0;
int k, ii, done_undo = 0;
Wireentry *wptr;
Instpinentry *iptr;
int sqx, sqy;
Str_hashtable coord_table = {NULL, 0}; /* hash table to add new wires at a given position only once */
char coord[200]; /* string representation of 'x0 y0' or 'pinx0 piny0' */
str_hash_init(&coord_table, HASHSIZE);
rebuild_selected_array();
k = xctx->lastsel;
prepare_netlist_structs(0); /* rebuild spatial hashes */
/* add wires to moving instance pins */
for(j=0;j<k; ++j) if(xctx->sel_array[j].type==ELEMENT) {
int inst = xctx->sel_array[j].n;
symbol = xctx->sym + xctx->inst[inst].ptr;
npin = symbol->rects[PINLAYER];
for(i=0;i<npin; ++i) {
get_inst_pin_coord(inst, i, &pinx0, &piny0);
get_square(pinx0, piny0, &sqx, &sqy);
iptr=xctx->instpin_spatial_table[sqx][sqy];
wptr=xctx->wire_spatial_table[sqx][sqy];
kissing=0;
while(iptr) {
ii = iptr->n;
if(ii == inst) {
iptr = iptr->next;
continue;
}
if( iptr->x0 == pinx0 && iptr->y0 == piny0 && xctx->inst[ii].sel == 0) {
kissing = 1;
break;
}
iptr = iptr->next;
}
while(wptr) {
xWire *w = &xctx->wire[wptr->n];
if( touch(w->x1, w->y1, w->x2, w->y2, pinx0, piny0) && w->sel == 0) {
kissing = 1;
break;
}
wptr = wptr->next;
}
if(kissing) {
if(!done_undo) {
xctx->push_undo();
done_undo = 1;
}
my_snprintf(coord, S(coord), "%.16g %.16g", pinx0, piny0);
if (str_hash_lookup(&coord_table, coord, "", XLOOKUP)==NULL) {
dbg(1, "connect_by_kissing(): adding wire in %g %g, wires before = %d\n", pinx0, piny0, xctx->wires);
str_hash_lookup(&coord_table, coord, "", XINSERT);
storeobject(-1, pinx0, piny0, pinx0, piny0, WIRE, 0, SELECTED1, NULL);
changed = 1;
xctx->prep_hash_wires=0;
xctx->need_reb_sel_arr = 1;
}
}
}
}
/* add wires to moving wire endpoints */
for(j=0; j < k; ++j) if(xctx->sel_array[j].type == WIRE) {
int wire = xctx->sel_array[j].n;
if(xctx->wire[wire].sel != SELECTED) continue; /* skip partially selected wires */
for(i=0;i<2; ++i) {
if(i == 0) {
x0 = xctx->wire[wire].x1;
y0 = xctx->wire[wire].y1;
} else {
x0 = xctx->wire[wire].x2;
y0 = xctx->wire[wire].y2;
}
get_square(x0, y0, &sqx, &sqy);
iptr=xctx->instpin_spatial_table[sqx][sqy];
wptr=xctx->wire_spatial_table[sqx][sqy];
kissing=0;
while(iptr) {
ii = iptr->n;
dbg(1, "connect_by_kissing(): ii=%d, x0=%g, y0=%g, iptr->x0=%g, iptr->y0=%g\n",
ii, x0, y0, iptr->x0, iptr->y0);
if( iptr->x0 == x0 && iptr->y0 == y0 && xctx->inst[ii].sel == 0) {
kissing = 1;
break;
}
iptr = iptr->next;
}
while(wptr) {
xWire *w = &xctx->wire[wptr->n];
if(wire == wptr->n) {
wptr = wptr->next;
continue;
}
if( touch(w->x1, w->y1, w->x2, w->y2, x0, y0) && w->sel == 0) {
kissing = 1;
break;
}
wptr = wptr->next;
}
if(kissing) {
if(!done_undo) {
xctx->push_undo();
done_undo = 1;
}
my_snprintf(coord, S(coord), "%.16g %.16g", x0, y0);
if (str_hash_lookup(&coord_table, coord, "", XLOOKUP)==NULL) {
dbg(1, "connect_by_kissing(): adding wire in %g %g, wires before = %d\n", x0, y0, xctx->wires);
str_hash_lookup(&coord_table, coord, "", XINSERT);
storeobject(-1, x0, y0, x0, y0, WIRE, 0, SELECTED1, NULL);
changed = 1;
xctx->prep_hash_wires=0;
xctx->need_reb_sel_arr = 1;
}
}
}
}
str_hash_free(&coord_table);
rebuild_selected_array();
return changed;
}
int unselect_partial_sel_wires(void)
{
xSymbol *symbol;
int npin, i, j;
double x0,y0, pinx0, piny0;
int changed = 0;
int k;
Wireentry *wptr;
int sqx, sqy;
if(!tclgetboolvar("unselect_partial_sel_wires")) return 0;
rebuild_selected_array();
k = xctx->lastsel;
prepare_netlist_structs(0); /* rebuild spatial hashes */
/* unselect wires attached to moved instance pins */
for(j=0;j<k; ++j) if(xctx->sel_array[j].type==ELEMENT) {
int inst = xctx->sel_array[j].n;
symbol = xctx->sym + xctx->inst[inst].ptr;
npin = symbol->rects[PINLAYER];
for(i=0;i<npin; ++i) {
get_inst_pin_coord(inst, i, &pinx0, &piny0);
get_square(pinx0, piny0, &sqx, &sqy);
wptr=xctx->wire_spatial_table[sqx][sqy];
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);
changed = 1;
}
wptr = wptr->next;
}
}
}
/* unselect wires attached to moved wire endpoints */
for(j=0; j < k; ++j) if(xctx->sel_array[j].type == WIRE) {
int wire = xctx->sel_array[j].n;
if(xctx->wire[wire].sel != SELECTED) continue; /* skip partially selected wires */
for(i=0;i<2; ++i) {
if(i == 0) {
x0 = xctx->wire[wire].x1;
y0 = xctx->wire[wire].y1;
} else {
x0 = xctx->wire[wire].x2;
y0 = xctx->wire[wire].y2;
}
get_square(x0, y0, &sqx, &sqy);
wptr=xctx->wire_spatial_table[sqx][sqy];
while(wptr) {
xWire *w = &xctx->wire[wptr->n];
if(wire == wptr->n) {
wptr = wptr->next;
continue;
}
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);
changed = 1;
}
wptr = wptr->next;
}
}
}
return changed;
}
void attach_labels_to_inst(int interactive) /* offloaded from callback.c 20171005 */
{
xSymbol *symbol;
int npin, i, j;
double x0,y0, pinx0, piny0;
short flip, rot, rot1 ;
xRect *rct;
char *labname=NULL;
char *prop=NULL; /* 20161122 overflow safe */
char *symname_pin = NULL;
char *symname_wire = NULL;
char *type=NULL;
short dir;
int k,ii, skip;
int do_all_inst=0;
const char *rot_txt;
int rotated_text=-1;
Wireentry *wptr;
Instpinentry *iptr;
int sqx, sqy;
int first_call;
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]"));
if(symname_pin && symname_wire) {
rebuild_selected_array();
k = xctx->lastsel;
first_call=1;
prepare_netlist_structs(0);
for(j=0;j<k; ++j) if(xctx->sel_array[j].type==ELEMENT) {
found=1;
my_strdup(_ALLOC_ID_, &prop, xctx->inst[xctx->sel_array[j].n].instname);
my_strcat(_ALLOC_ID_, &prop, "_");
tclsetvar("custom_label_prefix",prop);
if(interactive && !do_all_inst) {
dbg(1,"attach_labels_to_inst(): invoking tcl attach_labels_to_inst\n");
tcleval("attach_labels_to_inst");
if(!strcmp(tclgetvar("rcode"),"") ) {
bbox(END, 0., 0., 0., 0.);
my_free(_ALLOC_ID_, &prop);
return;
}
}
if(interactive == 0 ) {
tclsetvar("rcode", "yes");
tclsetvar("use_lab_wire", "0");
tclsetvar("use_label_prefix", "0");
tclsetvar("do_all_inst", "1");
tclsetvar("rotated_text", "0");
}
use_label_prefix = tclgetboolvar("use_label_prefix");
rot_txt = tclgetvar("rotated_text");
if(strcmp(rot_txt,"")) rotated_text=atoi(rot_txt);
my_strdup(_ALLOC_ID_, &type,(xctx->inst[xctx->sel_array[j].n].ptr+ xctx->sym)->type);
if( type && IS_LABEL_OR_PIN(type) ) {
continue;
}
if(!do_all_inst && tclgetboolvar("do_all_inst")) do_all_inst=1;
dbg(1, "attach_labels_to_inst(): 1--> %s %.16g %.16g %s\n",
xctx->inst[xctx->sel_array[j].n].name,
xctx->inst[xctx->sel_array[j].n].x0,
xctx->inst[xctx->sel_array[j].n].y0,
xctx->sym[xctx->inst[xctx->sel_array[j].n].ptr].name);
x0 = xctx->inst[xctx->sel_array[j].n].x0;
y0 = xctx->inst[xctx->sel_array[j].n].y0;
rot = xctx->inst[xctx->sel_array[j].n].rot;
flip = xctx->inst[xctx->sel_array[j].n].flip;
symbol = xctx->sym + xctx->inst[xctx->sel_array[j].n].ptr;
npin = symbol->rects[PINLAYER];
rct=symbol->rect[PINLAYER];
for(i=0;i<npin; ++i) {
my_strdup(_ALLOC_ID_, &labname,get_tok_value(rct[i].prop_ptr,"name",1));
dbg(1,"attach_labels_to_inst(): 2 --> labname=%s\n", labname);
pinx0 = (rct[i].x1+rct[i].x2)/2;
piny0 = (rct[i].y1+rct[i].y2)/2;
if(strcmp(get_tok_value(rct[i].prop_ptr,"dir",0),"in")) dir=1; /* out or inout pin */
else dir=0; /* input pin */
/* opin or iopin on left of symbol--> reverse orientation 20171205 */
if(rotated_text ==-1 && dir==1 && pinx0<0) dir=0;
ROTATION(rot, flip, 0.0, 0.0, pinx0, piny0, pinx0, piny0);
pinx0 += x0;
piny0 += y0;
get_square(pinx0, piny0, &sqx, &sqy);
iptr=xctx->instpin_spatial_table[sqx][sqy];
wptr=xctx->wire_spatial_table[sqx][sqy];
skip=0;
while(iptr) {
ii = iptr->n;
if(ii == xctx->sel_array[j].n) {
iptr = iptr->next;
continue;
}
if( iptr->x0 == pinx0 && iptr->y0 == piny0 ) {
skip=1;
break;
}
iptr = iptr->next;
}
while(wptr) {
if( touch(xctx->wire[wptr->n].x1, xctx->wire[wptr->n].y1,
xctx->wire[wptr->n].x2, xctx->wire[wptr->n].y2, pinx0, piny0) ) {
skip=1;
break;
}
wptr = wptr->next;
}
if(!skip) {
my_strdup(_ALLOC_ID_, &prop, "name=p1 lab=");
if(use_label_prefix) {
my_strcat(_ALLOC_ID_, &prop, (char *)tclgetvar("custom_label_prefix"));
}
/* /20171005 */
my_strcat(_ALLOC_ID_, &prop, labname);
dir ^= flip; /* 20101129 20111030 */
if(rotated_text ==-1) {
rot1=rot;
if(rot1==1 || rot1==2) { dir=!dir;rot1 = (short)((rot1+2) %4);}
} else {
rot1=(short)((rot+rotated_text)%4); /* 20111103 20171208 text_rotation */
}
if(!tclgetboolvar("use_lab_wire")) {
place_symbol(-1,symname_pin, pinx0, piny0, rot1, dir, prop, 2, first_call, 1/*to_push_undo*/);
} else {
place_symbol(-1,symname_wire, pinx0, piny0, rot1, dir, prop, 2, first_call, 1/*to_push_undo*/);
}
first_call=0;
}
dbg(1, "attach_labels_to_inst(): %d %.16g %.16g %s\n", i, pinx0, piny0,labname);
}
}
if(first_call == 0) set_modify(1);
my_free(_ALLOC_ID_, &prop);
my_free(_ALLOC_ID_, &labname);
my_free(_ALLOC_ID_, &type);
if(!found) return;
/* draw things */
if(!first_call) {
bbox(SET , 0.0 , 0.0 , 0.0 , 0.0);
draw();
bbox(END , 0.0 , 0.0 , 0.0 , 0.0);
}
} else {
fprintf(errfp, "attach_labels_to_inst(): location of schematic labels not found\n");
tcleval("alert_ {attach_labels_to_inst(): location of schematic labels not found} {}");
}
my_free(_ALLOC_ID_, &symname_pin);
my_free(_ALLOC_ID_, &symname_wire);
}
void delete_files(void)
{
char str[PATH_MAX + 100];
rebuild_selected_array();
if(xctx->lastsel && xctx->sel_array[0].type==ELEMENT) {
my_snprintf(str, S(str), "delete_files {%s}",
abs_sym_path(tcl_hook2(xctx->inst[xctx->sel_array[0].n].name), ""));
} else {
my_snprintf(str, S(str), "delete_files {%s}",
abs_sym_path(xctx->sch[xctx->currsch], ""));
}
tcleval(str);
}
void place_net_label(int type)
{
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*/);
} else {
const char *lab = tcleval("rel_sym_path [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);
xctx->ui_state |= START_SYMPIN;
}
/* draw_sym==4 select element after placing */
/* draw_sym==2 begin bbox if(first_call), add bbox */
/* draw_sym==1 begin bbox if(first_call), add bbox, end bbox, draw placed symbols */
/* */
/* first_call: set to 1 on first invocation for a given set of symbols (same prefix) */
/* set to 0 on next calls, this speeds up searching for unique names in prop string */
/* returns 1 if symbol successfully placed, 0 otherwise */
int place_symbol(int pos, const char *symbol_name, double x, double y, short rot, short flip,
const char *inst_props, int draw_sym, int first_call, int to_push_undo)
/* if symbol_name is a valid string load specified cell and */
/* use the given params, otherwise query user */
{
int i,j,n;
char name[PATH_MAX];
char name1[PATH_MAX];
char tclev = 0;
if(symbol_name==NULL) {
tcleval("load_file_dialog {Choose symbol} *.sym INITIALINSTDIR");
my_strncpy(name1, tclresult(), S(name1));
} else {
my_strncpy(name1, symbol_name, S(name1));
}
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(")) {
tclev = 1;
my_snprintf(name1, S(name1), "%s", str_replace(name1, "tcleval(", "", 0));
}
dbg(1, "place_symbol(): 2: name1=%s\n",name1);
tclvareval("is_xschem_file {", name1, "}", NULL);
if(!strcmp(tclresult(), "GENERATOR")) {
my_snprintf(name, S(name), "%s()", name1);
} else {
my_strncpy(name, name1, S(name));
}
my_strncpy(name1, rel_sym_path(name), S(name1));
/* ... and re-add tcleval( around relative path symbol name */
if(tclev) {
my_snprintf(name, S(name), "tcleval(%s", name1);
} else {
my_strncpy(name, name1, S(name));
}
if(name[0]) {
if(first_call && to_push_undo) xctx->push_undo();
} else return 0;
i=match_symbol(name);
if(i!=-1)
{
if(first_call) hash_names(-1, XINSERT);
check_inst_storage();
if(pos==-1 || pos > xctx->instances) n=xctx->instances;
else
{
xctx->prep_hash_inst = 0; /* instances moved so need to rebuild hash */
for(j=xctx->instances;j>pos;j--)
{
xctx->inst[j]=xctx->inst[j-1];
}
n=pos;
}
/* 03-02-2000 */
dbg(1, "place_symbol(): checked inst_ptr storage, sym number i=%d\n", i);
xctx->inst[n].ptr = i;
xctx->inst[n].name=NULL;
xctx->inst[n].lab=NULL;
dbg(1, "place_symbol(): entering my_strdup: name=%s\n",name); /* 03-02-2000 */
my_strdup2(_ALLOC_ID_, &xctx->inst[n].name ,name);
dbg(1, "place_symbol(): done my_strdup: name=%s\n",name); /* 03-02-2000 */
/* xctx->inst[n].x0=symbol_name ? x : xctx->mousex_snap; */
/* xctx->inst[n].y0=symbol_name ? y : xctx->mousey_snap; */
xctx->inst[n].x0= x ; /* 20070228 x and y given in callback */
xctx->inst[n].y0= y ;
xctx->inst[n].rot=symbol_name ? rot : 0;
xctx->inst[n].flip=symbol_name ? flip : 0;
xctx->inst[n].flags=0;
xctx->inst[n].color=-10000; /* small negative values used for simulation */
xctx->inst[n].sel=0;
xctx->inst[n].node=NULL;
xctx->inst[n].prop_ptr=NULL;
xctx->inst[n].instname=NULL;
dbg(1, "place_symbol() :all inst_ptr members set\n"); /* 03-02-2000 */
if(inst_props) {
new_prop_string(n, inst_props,!first_call, tclgetboolvar("disable_unique_names")); /* 20171214 first_call */
}
else {
set_inst_prop(n); /* no props, get from sym template, also calls new_prop_string() */
}
dbg(1, "place_symbol(): done set_inst_prop()\n"); /* 03-02-2000 */
/* 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);
xctx->inst[n].ptr = i;
set_inst_flags(&xctx->inst[n]);
hash_names(n, XINSERT);
if(first_call && (draw_sym & 3) ) bbox(START, 0.0 , 0.0 , 0.0 , 0.0);
xctx->instances++; /* must be updated before calling symbol_bbox() */
/* force these vars to 0 to trigger a prepare_netlist_structs(0) needed by symbol_bbox->translate
* to translate @#n:net_name texts */
xctx->prep_net_structs=0;
xctx->prep_hi_structs=0;
symbol_bbox(n, &xctx->inst[n].x1, &xctx->inst[n].y1,
&xctx->inst[n].x2, &xctx->inst[n].y2);
if(xctx->prep_hash_inst) hash_inst(XINSERT, n); /* no need to rehash, add item */
/* xctx->prep_hash_inst=0; */
if(draw_sym & 3) {
bbox(ADD, xctx->inst[n].x1, xctx->inst[n].y1, xctx->inst[n].x2, xctx->inst[n].y2);
}
if(draw_sym&1) {
bbox(SET , 0.0 , 0.0 , 0.0 , 0.0);
draw();
bbox(END , 0.0 , 0.0 , 0.0 , 0.0);
}
/* hilight new element 24122002 */
if(draw_sym & 4 ) {
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);
drawtempline(xctx->gc[SELLAYER], END, 0.0, 0.0, 0.0, 0.0);
xctx->need_reb_sel_arr = 1;
rebuild_selected_array(); /* sets xctx->ui_state |= SELECTION; */
}
}
return 1;
}
void symbol_in_new_window(int new_process)
{
char filename[PATH_MAX];
char win_path[WINDOW_PATH_SIZE];
rebuild_selected_array();
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, 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, 1);
}
}
}
/* 20111007 duplicate current schematic if no inst selected */
void schematic_in_new_window(int new_process)
{
char filename[PATH_MAX];
char win_path[WINDOW_PATH_SIZE];
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], 1);
}
else {
if( /* do not descend if not subcircuit */
(xctx->inst[xctx->sel_array[0].n].ptr+ xctx->sym)->type &&
strcmp(
(xctx->inst[xctx->sel_array[0].n].ptr+ xctx->sym)->type,
"subcircuit"
) &&
strcmp(
(xctx->inst[xctx->sel_array[0].n].ptr+ xctx->sym)->type,
"primitive"
)
) return;
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, 1);
}
}
}
void launcher(void)
{
const char *url;
char program[PATH_MAX];
int n;
rebuild_selected_array();
if(xctx->lastsel ==1 && xctx->sel_array[0].type==ELEMENT)
{
double mx=xctx->mousex, my=xctx->mousey;
select_object(mx,my,SELECTED, 0);
tcleval("update; after 300");
select_object(mx,my,0, 0);
n=xctx->sel_array[0].n;
my_strncpy(program, get_tok_value(xctx->inst[n].prop_ptr,"program",0), S(program)); /* handle backslashes */
url = get_tok_value(xctx->inst[n].prop_ptr,"url",0); /* handle backslashes */
dbg(1, "launcher(): url=%s\n", url);
if(url[0] || (program[0])) { /* open url with appropriate program */
tclvareval("launcher {", url, "} {", program, "}", NULL);
} else {
my_strncpy(program, get_tok_value(xctx->inst[n].prop_ptr,"tclcommand",0), S(program));
if(program[0]) { /* execute tcl command */
tcleval(program);
}
}
}
}
/* get symbol reference of instance 'inst', looking into
* instance 'schematic' attribute (and appending '.sym') if set
* or get it from inst[inst].name.
* perform tcl substitution of the result and
* return the last 'ndir' directory components of symbol reference. */
const char *get_sym_name(int inst, int ndir, int ext)
{
const char *sym, *sch;
/* instance based symbol selection */
sch = tcl_hook2(str_replace(get_tok_value(xctx->inst[inst].prop_ptr,"schematic", 2), "@symname",
get_cell(xctx->inst[inst].name, 0), '\\'));
if(xctx->tok_size) { /* token exists */
sym = add_ext(rel_sym_path(sch), ".sym");
}
else {
sym = tcl_hook2(xctx->inst[inst].name);
}
if(ext) return get_cell_w_ext(sym, ndir);
else return get_cell(sym, ndir);
}
void copy_symbol(xSymbol *dest_sym, xSymbol *src_sym)
{
int c, j;
dest_sym->minx = src_sym->minx;
dest_sym->maxx = src_sym->maxx;
dest_sym->miny = src_sym->miny;
dest_sym->maxy = src_sym->maxy;
dest_sym->flags = src_sym->flags;
dest_sym->texts = src_sym->texts;
dest_sym->name = NULL;
dest_sym->base_name = NULL; /* this is not allocated and points to the base symbol */
dest_sym->prop_ptr = NULL;
dest_sym->type = NULL;
dest_sym->templ = NULL;
my_strdup2(_ALLOC_ID_, &dest_sym->name, src_sym->name);
my_strdup2(_ALLOC_ID_, &dest_sym->type, src_sym->type);
my_strdup2(_ALLOC_ID_, &dest_sym->templ, src_sym->templ);
my_strdup2(_ALLOC_ID_, &dest_sym->prop_ptr, src_sym->prop_ptr);
dest_sym->line = my_calloc(_ALLOC_ID_, cadlayers, sizeof(xLine *));
dest_sym->poly = my_calloc(_ALLOC_ID_, cadlayers, sizeof(xPoly *));
dest_sym->arc = my_calloc(_ALLOC_ID_, cadlayers, sizeof(xArc *));
dest_sym->rect = my_calloc(_ALLOC_ID_, cadlayers, sizeof(xRect *));
dest_sym->lines = my_calloc(_ALLOC_ID_, cadlayers, sizeof(int));
dest_sym->rects = my_calloc(_ALLOC_ID_, cadlayers, sizeof(int));
dest_sym->arcs = my_calloc(_ALLOC_ID_, cadlayers, sizeof(int));
dest_sym->polygons = my_calloc(_ALLOC_ID_, cadlayers, sizeof(int));
dest_sym->text = my_calloc(_ALLOC_ID_, src_sym->texts, sizeof(xText));
memcpy(dest_sym->lines, src_sym->lines, sizeof(dest_sym->lines[0]) * cadlayers);
memcpy(dest_sym->rects, src_sym->rects, sizeof(dest_sym->rects[0]) * cadlayers);
memcpy(dest_sym->arcs, src_sym->arcs, sizeof(dest_sym->arcs[0]) * cadlayers);
memcpy(dest_sym->polygons, src_sym->polygons, sizeof(dest_sym->polygons[0]) * cadlayers);
for(c = 0;c<cadlayers; ++c) {
/* symbol lines */
dest_sym->line[c] = my_calloc(_ALLOC_ID_, src_sym->lines[c], sizeof(xLine));
for(j = 0; j < src_sym->lines[c]; ++j) {
dest_sym->line[c][j] = src_sym->line[c][j];
dest_sym->line[c][j].prop_ptr = NULL;
my_strdup(_ALLOC_ID_, &dest_sym->line[c][j].prop_ptr, src_sym->line[c][j].prop_ptr);
}
/* symbol rects */
dest_sym->rect[c] = my_calloc(_ALLOC_ID_, src_sym->rects[c], sizeof(xRect));
for(j = 0; j < src_sym->rects[c]; ++j) {
dest_sym->rect[c][j] = src_sym->rect[c][j];
dest_sym->rect[c][j].prop_ptr = NULL;
dest_sym->rect[c][j].extraptr = NULL;
my_strdup(_ALLOC_ID_, &dest_sym->rect[c][j].prop_ptr, src_sym->rect[c][j].prop_ptr);
}
/* symbol arcs */
dest_sym->arc[c] = my_calloc(_ALLOC_ID_, src_sym->arcs[c], sizeof(xArc));
for(j = 0; j < src_sym->arcs[c]; ++j) {
dest_sym->arc[c][j] = src_sym->arc[c][j];
dest_sym->arc[c][j].prop_ptr = NULL;
my_strdup(_ALLOC_ID_, &dest_sym->arc[c][j].prop_ptr, src_sym->arc[c][j].prop_ptr);
}
/* symbol polygons */
dest_sym->poly[c] = my_calloc(_ALLOC_ID_, src_sym->polygons[c], sizeof(xPoly));
for(j = 0; j < src_sym->polygons[c]; ++j) {
int points = src_sym->poly[c][j].points;
dest_sym->poly[c][j] = src_sym->poly[c][j];
dest_sym->poly[c][j].prop_ptr = NULL;
dest_sym->poly[c][j].x = my_malloc(_ALLOC_ID_, points * sizeof(double));
dest_sym->poly[c][j].y = my_malloc(_ALLOC_ID_, points * sizeof(double));
dest_sym->poly[c][j].selected_point = my_malloc(_ALLOC_ID_, points * sizeof(unsigned short));
my_strdup(_ALLOC_ID_, &dest_sym->poly[c][j].prop_ptr, src_sym->poly[c][j].prop_ptr);
memcpy(dest_sym->poly[c][j].x, src_sym->poly[c][j].x, points * sizeof(double));
memcpy(dest_sym->poly[c][j].y, src_sym->poly[c][j].y, points * sizeof(double));
memcpy(dest_sym->poly[c][j].selected_point, src_sym->poly[c][j].selected_point,
points * sizeof(unsigned short));
}
}
/* symbol texts */
for(j = 0; j < src_sym->texts; ++j) {
dest_sym->text[j] = src_sym->text[j];
dest_sym->text[j].prop_ptr = NULL;
dest_sym->text[j].txt_ptr = NULL;
dest_sym->text[j].font = NULL;
dest_sym->text[j].floater_instname = NULL;
dest_sym->text[j].floater_ptr = NULL;
my_strdup2(_ALLOC_ID_, &dest_sym->text[j].prop_ptr, src_sym->text[j].prop_ptr);
my_strdup2(_ALLOC_ID_, &dest_sym->text[j].floater_ptr, src_sym->text[j].floater_ptr);
dbg(1, "copy_symbol1(): allocating sym %d text %d\n", dest_sym - xctx->sym, j);
my_strdup2(_ALLOC_ID_, &dest_sym->text[j].txt_ptr, src_sym->text[j].txt_ptr);
my_strdup2(_ALLOC_ID_, &dest_sym->text[j].font, src_sym->text[j].font);
my_strdup2(_ALLOC_ID_, &dest_sym->text[j].floater_instname, src_sym->text[j].floater_instname);
}
}
/* what = 1: start
* what = 0 : end : should NOT be called if match_symbol() has been executed between start & end
*/
void get_additional_symbols(int what)
{
int i;
static int num_syms; /* no context switch between start and end so it is safe */
Int_hashentry *found;
Int_hashtable sym_table = {NULL, 0};
if(what == 1) { /* start */
int_hash_init(&sym_table, HASHSIZE);
num_syms = xctx->symbols;
for(i = 0; i < xctx->symbols; ++i) {
int_hash_lookup(&sym_table, xctx->sym[i].name, i, XINSERT);
}
/* handle instances with "schematic=..." attribute (polymorphic symbols) */
for(i=0;i<xctx->instances; ++i) {
char *spice_sym_def = NULL;
char *vhdl_sym_def = NULL;
char *verilog_sym_def = NULL;
char *sch = NULL;
my_strdup(_ALLOC_ID_, &spice_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"spice_sym_def",0));
my_strdup(_ALLOC_ID_, &verilog_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"verilog_sym_def",0));
my_strdup(_ALLOC_ID_, &vhdl_sym_def, get_tok_value(xctx->inst[i].prop_ptr,"vhdl_sym_def",0));
my_strdup2(_ALLOC_ID_, &sch, tcl_hook2(
str_replace( get_tok_value(xctx->inst[i].prop_ptr,"schematic",2), "@symname",
get_cell(xctx->inst[i].name, 0), '\\')));
dbg(1, "get_additional_symbols(): sch=%s\n", sch);
if(xctx->tok_size) { /* token exists */
int j;
char *sym = NULL;
dbg(1, "get_additional_symbols(): inst=%d, sch=%s\n", i, sch);
if(is_generator(sch)) {
my_strdup2(_ALLOC_ID_, &sym, sch);
dbg(1, "get_additional_symbols(): generator\n");
} else {
my_strdup2(_ALLOC_ID_, &sym, add_ext(rel_sym_path(sch), ".sym"));
}
found = int_hash_lookup(&sym_table, sym, 0, XLOOKUP);
if(!found) {
j = xctx->symbols;
int_hash_lookup(&sym_table, sym, j, XINSERT);
dbg(1, "get_additional_symbols(): adding symbol %s\n", sym);
check_symbol_storage();
copy_symbol(&xctx->sym[j], xctx->inst[i].ptr + xctx->sym);
xctx->sym[j].base_name = (xctx->inst[i].ptr + xctx->sym)->name;
my_strdup(_ALLOC_ID_, &xctx->sym[j].name, sym);
my_free(_ALLOC_ID_, &sym);
if(spice_sym_def)
my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr,
subst_token(xctx->sym[j].prop_ptr, "spice_sym_def", spice_sym_def));
if(verilog_sym_def)
my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr,
subst_token(xctx->sym[j].prop_ptr, "verilog_sym_def", verilog_sym_def));
if(vhdl_sym_def)
my_strdup(_ALLOC_ID_, &xctx->sym[j].prop_ptr,
subst_token(xctx->sym[j].prop_ptr, "vhdl_sym_def", vhdl_sym_def));
xctx->symbols++;
} else {
j = found->value;
}
}
my_free(_ALLOC_ID_, &sch);
my_free(_ALLOC_ID_, &spice_sym_def);
my_free(_ALLOC_ID_, &vhdl_sym_def);
my_free(_ALLOC_ID_, &verilog_sym_def);
}
int_hash_free(&sym_table);
} else { /* end */
for(i = xctx->symbols - 1; i >= num_syms; --i) {
remove_symbol(i);
}
xctx->symbols = num_syms;
}
}
void get_sch_from_sym(char *filename, xSymbol *sym, int inst)
{
char *sch = NULL;
char *str_tmp = NULL;
int web_url = 0;
struct stat buf;
/* get sch/sym name from parent schematic downloaded from web */
if(is_from_web(xctx->current_dirname)) {
web_url = 1;
}
dbg(1, "get_sch_from_sym(): current_dirname= %s\n", xctx->current_dirname);
dbg(1, "get_sch_from_sym(): symbol %s inst=%d web_url=%d\n", sym->name, inst, web_url);
if(inst >= 0) my_strdup(_ALLOC_ID_, &str_tmp, get_tok_value(xctx->inst[inst].prop_ptr, "schematic", 2));
if(!str_tmp) my_strdup2(_ALLOC_ID_, &str_tmp, get_tok_value(sym->prop_ptr, "schematic", 2));
if(str_tmp[0]) { /* schematic attribute in symbol or instance was given */
/* @symname in schematic attribute will be replaced with symbol name */
my_strdup2(_ALLOC_ID_, &sch, tcl_hook2(str_replace(str_tmp, "@symname",
get_cell(sym->name, 0), '\\')));
if(is_generator(sch)) { /* generator: return as is */
my_strncpy(filename, sch, PATH_MAX);
dbg(1, "get_sch_from_sym(): filename=%s\n", filename);
} else { /* not generator */
dbg(1, "get_sch_from_sym(): after tcl_hook2 sch=%s\n", sch);
/* for schematics referenced from web symbols do not build absolute path */
if(web_url) my_strncpy(filename, sch, PATH_MAX);
else my_strncpy(filename, abs_sym_path(sch, ""), PATH_MAX);
}
} else { /* no schematic attribute from instance or symbol */
const char *symname_tcl = tcl_hook2(sym->name);
if(is_generator(symname_tcl)) my_strncpy(filename, symname_tcl, PATH_MAX);
else if(tclgetboolvar("search_schematic")) {
/* for schematics referenced from web symbols do not build absolute path */
if(web_url) my_strncpy(filename, add_ext(sym->name, ".sch"), PATH_MAX);
else my_strncpy(filename, abs_sym_path(sym->name, ".sch"), PATH_MAX);
} else {
/* for schematics referenced from web symbols do not build absolute path */
if(web_url) my_strncpy(filename, add_ext(sym->name, ".sch"), PATH_MAX);
else {
if(!stat(abs_sym_path(sym->name, ""), &buf)) /* symbol exists. pretend schematic exists too ... */
my_strncpy(filename, add_ext(abs_sym_path(sym->name, ""), ".sch"), PATH_MAX);
else /* ... symbol does not exist (instances with schematic=... attr) so can not pretend that */
my_strncpy(filename, abs_sym_path(sym->name, ".sch"), PATH_MAX);
}
}
}
if(sch) my_free(_ALLOC_ID_, &sch);
if(web_url) {
char sympath[PATH_MAX];
/* build local cached filename of web_url */
my_snprintf(sympath, S(sympath), "%s/xschem_web/%s", tclgetvar("XSCHEM_TMP_DIR"), get_cell_w_ext(filename, 0));
if(stat(sympath, &buf)) { /* not found, download */
/* download item into ${XSCHEM_TMP_DIR}/xschem_web */
tclvareval("try_download_url {", xctx->current_dirname, "} {", filename, "}", NULL);
}
if(stat(sympath, &buf)) { /* not found !!! build abs_sym_path to look into local fs and hope fror the best */
my_strncpy(filename, abs_sym_path(sym->name, ".sch"), PATH_MAX);
} else {
my_strncpy(filename, sympath, PATH_MAX);
}
}
my_free(_ALLOC_ID_, &str_tmp);
dbg(1, "get_sch_from_sym(): sym->name=%s, filename=%s\n", sym->name, filename);
}
int descend_schematic(int instnumber)
{
char *str = NULL;
char filename[PATH_MAX];
int inst_mult, inst_number;
int save_ok = 0;
int i, n = 0;
rebuild_selected_array();
if(xctx->lastsel !=1 || xctx->sel_array[0].type!=ELEMENT) {
dbg(1, "descend_schematic(): wrong selection\n");
return 0;
}
else {
/* no name set for current schematic: save it before descending*/
if(!strcmp(xctx->sch[xctx->currsch],""))
{
char cmd[PATH_MAX+1000];
char res[PATH_MAX];
my_strncpy(filename, xctx->sch[xctx->currsch], S(filename));
my_snprintf(cmd, S(cmd), "save_file_dialog {Save file} *.\\{sch,sym\\} INITIALLOADDIR {%s}", filename);
tcleval(cmd);
my_strncpy(res, tclresult(), S(res));
if(!res[0]) return 0;
dbg(1, "descend_schematic(): saving: %s\n",res);
save_ok = save_schematic(res);
if(save_ok==0) return 0;
}
n = xctx->sel_array[0].n;
dbg(1, "descend_schematic(): selected:%s\n", xctx->inst[n].name);
dbg(1, "descend_schematic(): inst type: %s\n", (xctx->inst[n].ptr+ xctx->sym)->type);
if( /* do not descend if not subcircuit */
(xctx->inst[n].ptr+ xctx->sym)->type &&
strcmp( (xctx->inst[n].ptr+ xctx->sym)->type, "subcircuit") &&
strcmp( (xctx->inst[n].ptr+ xctx->sym)->type, "primitive")
) return 0;
if(xctx->modified) {
int ret;
ret = save(1);
/* if circuit is changed but not saved before descending
* state will be inconsistent when returning, can not propagare hilights
* save() return value:
* 1 : file saved
* -1 : user cancel
* 0 : file not saved due to errors or per user request
*/
if(ret == 0) clear_all_hilights();
if(ret == -1) return 0; /* user cancel */
}
/* build up current hierarchy path */
dbg(1, "descend_schematic(): selected instname=%s\n", xctx->inst[n].instname);
if(xctx->inst[n].instname && xctx->inst[n].instname[0]) {
my_strdup2(_ALLOC_ID_, &str, expandlabel(xctx->inst[n].instname, &inst_mult));
} else {
my_strdup2(_ALLOC_ID_, &str, "");
inst_mult = 1;
}
prepare_netlist_structs(0); /* for portmap feature (mapping subcircuit nodes connected to
* ports to upper level) */
inst_number = 1;
if(inst_mult > 1) { /* on multiple instances ask where to descend, to correctly evaluate
the hierarchy path you descend to */
if(instnumber == 0 ) {
const char *inum;
tclvareval("input_line ", "{input instance number (leftmost = 1) to descend into:\n"
"negative numbers select instance starting\nfrom the right (rightmost = -1)}"
" {} 1 6", NULL);
inum = tclresult();
dbg(1, "descend_schematic(): inum=%s\n", inum);
if(!inum[0]) {
my_free(_ALLOC_ID_, &str);
return 0;
}
inst_number=atoi(inum);
} else {
inst_number = instnumber;
}
if(inst_number < 0 ) inst_number += inst_mult+1;
/* any invalid number->descend to leftmost inst */
if(inst_number <1 || inst_number > inst_mult) inst_number = 1;
}
my_strdup(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], xctx->sch_path[xctx->currsch]);
xctx->sch_path_hash[xctx->currsch+1] =0;
if(xctx->portmap[xctx->currsch + 1].table) str_hash_free(&xctx->portmap[xctx->currsch + 1]);
str_hash_init(&xctx->portmap[xctx->currsch + 1], HASHSIZE);
for(i = 0; i < xctx->sym[xctx->inst[n].ptr].rects[PINLAYER]; i++) {
const char *pin_name = get_tok_value(xctx->sym[xctx->inst[n].ptr].rect[PINLAYER][i].prop_ptr,"name",0);
char *pin_node = NULL, *net_node = NULL;
int k, mult, net_mult;
char *single_p, *single_n = NULL, *single_n_ptr = NULL;
char *p_n_s1 = NULL;
char *p_n_s2 = NULL;
if(!pin_name[0]) continue;
if(!xctx->inst[n].node[i]) continue;
my_strdup2(_ALLOC_ID_, &pin_node, expandlabel(pin_name, &mult));
my_strdup2(_ALLOC_ID_, &net_node, expandlabel(xctx->inst[n].node[i], &net_mult));
p_n_s1 = pin_node;
for(k = 1; k<=mult; ++k) {
single_p = my_strtok_r(p_n_s1, ",", "", 0, &p_n_s2);
p_n_s1 = NULL;
my_strdup2(_ALLOC_ID_, &single_n,
find_nth(net_node, ",", "", 0, ((inst_number - 1) * mult + k - 1) % net_mult + 1));
single_n_ptr = single_n;
if(single_n_ptr[0] == '#') {
if(mult > 1) {
my_mstrcat(_ALLOC_ID_, &single_n, "[", my_itoa((inst_mult - inst_number + 1) * mult - k), "]", NULL);
}
single_n_ptr = single_n + 1;
}
str_hash_lookup(&xctx->portmap[xctx->currsch + 1], single_p, single_n_ptr, XINSERT);
dbg(1, "descend_schematic(): %s: %s ->%s\n", xctx->inst[n].instname, single_p, single_n_ptr);
}
if(single_n) my_free(_ALLOC_ID_, &single_n);
my_free(_ALLOC_ID_, &net_node);
my_free(_ALLOC_ID_, &pin_node);
}
my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].prop_ptr,
xctx->inst[n].prop_ptr);
my_strdup(_ALLOC_ID_, &xctx->hier_attr[xctx->currsch].templ,
get_tok_value((xctx->inst[n].ptr+ xctx->sym)->prop_ptr, "template", 0));
dbg(1,"descend_schematic(): inst_number=%d\n", inst_number);
my_strcat(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], find_nth(str, ",", "", 0, inst_number));
my_free(_ALLOC_ID_, &str);
dbg(1,"descend_schematic(): inst_number=%d\n", inst_number);
my_strcat(_ALLOC_ID_, &xctx->sch_path[xctx->currsch+1], ".");
xctx->sch_inst_number[xctx->currsch] = inst_number;
dbg(1, "descend_schematic(): current path: %s\n", xctx->sch_path[xctx->currsch+1]);
dbg(1, "descend_schematic(): inst_number=%d\n", inst_number);
xctx->previous_instance[xctx->currsch]=n;
xctx->zoom_array[xctx->currsch].x=xctx->xorigin;
xctx->zoom_array[xctx->currsch].y=xctx->yorigin;
xctx->zoom_array[xctx->currsch].zoom=xctx->zoom;
xctx->currsch++;
hilight_child_pins();
unselect_all(1);
get_sch_from_sym(filename, xctx->inst[n].ptr+ xctx->sym, n);
dbg(1, "descend_schematic(): filename=%s\n", filename);
/* we are descending from a parent schematic downloaded from the web */
remove_symbols();
load_schematic(1, filename, 1, 1);
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);
zoom_full(1, 0, 1 + 2 * tclgetboolvar("zoom_full_center"), 0.97);
}
return 1;
}
void go_back(int confirm) /* 20171006 add confirm */
{
int save_ok;
int from_embedded_sym;
int save_modified;
char filename[PATH_MAX];
int prev_sch_type;
save_ok=1;
dbg(1,"go_back(): sch[xctx->currsch]=%s\n", xctx->sch[xctx->currsch]);
prev_sch_type = xctx->netlist_type; /* if CAD_SYMBOL_ATTRS do not hilight_parent_pins */
if(xctx->currsch>0)
{
/* if current sym/schematic is changed ask save before going up */
if(xctx->modified)
{
if(confirm) {
tcleval("ask_save_optional");
if(!strcmp(tclresult(), "yes") ) save_ok = save_schematic(xctx->sch[xctx->currsch]);
else if(!strcmp(tclresult(), "") ) return;
} else {
save_ok = save_schematic(xctx->sch[xctx->currsch]);
}
}
if(save_ok==0) return;
unselect_all(1);
remove_symbols();
from_embedded_sym=0;
if(strstr(xctx->sch[xctx->currsch], ".xschem_embedded_")) {
/* when returning after editing an embedded symbol
* load immediately symbol definition before going back (.xschem_embedded... file will be lost)
*/
load_sym_def(xctx->sch[xctx->currsch], NULL);
from_embedded_sym=1;
}
my_free(_ALLOC_ID_, &xctx->sch[xctx->currsch]);
if(xctx->portmap[xctx->currsch].table) str_hash_free(&xctx->portmap[xctx->currsch]);
xctx->sch_path_hash[xctx->currsch] = 0;
xctx->currsch--;
save_modified = xctx->modified; /* we propagate modified flag (cleared by load_schematic */
/* by default) to parent schematic if going back from embedded symbol */
my_strncpy(filename, xctx->sch[xctx->currsch], S(filename));
load_schematic(1, filename, 1, 1);
/* if we are returning from a symbol created from a generator don't set modified flag on parent
* as these symbols can not be edited / saved as embedded
* xctx->sch_inst_number[xctx->currsch + 1] == -1 --> we came from an inst with no embed flag set */
if(from_embedded_sym && xctx->sch_inst_number[xctx->currsch] != -1)
xctx->modified=save_modified; /* to force ask save embedded sym in parent schematic */
if(xctx->hilight_nets) {
if(prev_sch_type != CAD_SYMBOL_ATTRS) hilight_parent_pins();
propagate_hilights(1, 1, XINSERT_NOREPLACE);
}
xctx->xorigin=xctx->zoom_array[xctx->currsch].x;
xctx->yorigin=xctx->zoom_array[xctx->currsch].y;
xctx->zoom=xctx->zoom_array[xctx->currsch].zoom;
xctx->mooz=1/xctx->zoom;
change_linewidth(-1.);
draw();
dbg(1, "go_back(): current path: %s\n", xctx->sch_path[xctx->currsch]);
}
}
void clear_schematic(int cancel, int symbol)
{
if(cancel == 1) cancel=save(1);
if(cancel != -1) { /* -1 means user cancel save request */
char name[PATH_MAX];
struct stat buf;
int i;
xctx->currsch = 0;
unselect_all(1);
remove_symbols();
clear_drawing();
if(symbol == 1) {
xctx->netlist_type = CAD_SYMBOL_ATTRS;
set_tcl_netlist_type();
for(i=0;; ++i) { /* find a non-existent untitled[-n].sym */
if(i == 0) my_snprintf(name, S(name), "%s.sym", "untitled");
else my_snprintf(name, S(name), "%s-%d.sym", "untitled", i);
if(stat(name, &buf)) break;
}
my_free(_ALLOC_ID_, &xctx->sch[xctx->currsch]);
my_mstrcat(_ALLOC_ID_, &xctx->sch[xctx->currsch], pwd_dir, "/", name, NULL);
my_strncpy(xctx->current_name, name, S(xctx->current_name));
} else {
xctx->netlist_type = CAD_SPICE_NETLIST;
set_tcl_netlist_type();
for(i=0;; ++i) {
if(i == 0) my_snprintf(name, S(name), "%s.sch", "untitled");
else my_snprintf(name, S(name), "%s-%d.sch", "untitled", i);
if(stat(name, &buf)) break;
}
my_free(_ALLOC_ID_, &xctx->sch[xctx->currsch]);
my_mstrcat(_ALLOC_ID_, &xctx->sch[xctx->currsch], pwd_dir, "/", name, NULL);
my_strncpy(xctx->current_name, name, S(xctx->current_name));
}
draw();
set_modify(0);
xctx->prep_hash_inst=0;
xctx->prep_hash_wires=0;
xctx->prep_net_structs=0;
xctx->prep_hi_structs=0;
if(has_x) {
set_modify(-1);
}
}
}
#ifndef __unix__
/* Source: https://www.tcl.tk/man/tcl8.7/TclCmd/glob.htm */
/* backslash character has a special meaning to glob command,
so glob patterns containing Windows style path separators need special care.*/
void change_to_unix_fn(char* fn)
{
size_t len, i, ii;
len = strlen(fn);
ii = 0;
for (i = 0; i < len; ++i) {
if (fn[i]!='\\') fn[ii++] = fn[i];
else { fn[ii++] = '/'; if (fn[i + 1] == '\\') ++i; }
}
}
#endif
/* selected: 0 -> all, 1 -> selected, 2 -> hilighted */
void calc_drawing_bbox(xRect *boundbox, int selected)
{
xRect rect;
int c, i;
int count=0;
#if HAS_CAIRO==1
int customfont;
#endif
boundbox->x1=-100;
boundbox->x2=100;
boundbox->y1=-100;
boundbox->y2=100;
if(selected != 2) for(c=0;c<cadlayers; ++c)
{
int hide_graphs = tclgetboolvar("hide_empty_graphs");
int waves = (sch_waves_loaded() >= 0);
for(i=0;i<xctx->lines[c]; ++i)
{
if(selected == 1 && !xctx->line[c][i].sel) continue;
rect.x1=xctx->line[c][i].x1;
rect.x2=xctx->line[c][i].x2;
rect.y1=xctx->line[c][i].y1;
rect.y2=xctx->line[c][i].y2;
++count;
updatebbox(count,boundbox,&rect);
}
for(i=0;i<xctx->polygons[c]; ++i)
{
double x1=0., y1=0., x2=0., y2=0.;
int k;
if(selected == 1 && !xctx->poly[c][i].sel) continue;
++count;
for(k=0; k<xctx->poly[c][i].points; ++k) {
/* fprintf(errfp, " poly: point %d: %.16g %.16g\n", k, pp[c][i].x[k], pp[c][i].y[k]); */
if(k==0 || xctx->poly[c][i].x[k] < x1) x1 = xctx->poly[c][i].x[k];
if(k==0 || xctx->poly[c][i].y[k] < y1) y1 = xctx->poly[c][i].y[k];
if(k==0 || xctx->poly[c][i].x[k] > x2) x2 = xctx->poly[c][i].x[k];
if(k==0 || xctx->poly[c][i].y[k] > y2) y2 = xctx->poly[c][i].y[k];
}
rect.x1=x1;rect.y1=y1;rect.x2=x2;rect.y2=y2;
updatebbox(count,boundbox,&rect);
}
for(i=0;i<xctx->arcs[c]; ++i)
{
if(selected == 1 && !xctx->arc[c][i].sel) continue;
arc_bbox(xctx->arc[c][i].x, xctx->arc[c][i].y, xctx->arc[c][i].r, xctx->arc[c][i].a, xctx->arc[c][i].b,
&rect.x1, &rect.y1, &rect.x2, &rect.y2);
++count;
updatebbox(count,boundbox,&rect);
}
for(i=0;i<xctx->rects[c]; ++i)
{
if(selected == 1 && !xctx->rect[c][i].sel) continue;
/* skip graph objects if no datafile loaded */
if(c == GRIDLAYER && xctx->rect[c][i].flags) {
if(hide_graphs && !waves) continue;
}
rect.x1=xctx->rect[c][i].x1;
rect.x2=xctx->rect[c][i].x2;
rect.y1=xctx->rect[c][i].y1;
rect.y2=xctx->rect[c][i].y2;
++count;
updatebbox(count,boundbox,&rect);
}
}
if(selected == 2 && xctx->hilight_nets) prepare_netlist_structs(0);
for(i=0;i<xctx->wires; ++i)
{
double ov, y1, y2;
if(selected == 1 && !xctx->wire[i].sel) continue;
if(selected == 2) {
/* const char *str;
* str = get_tok_value(xctx->wire[i].prop_ptr, "lab",0);
* if(!str[0] || !bus_hilight_hash_lookup(str, 0,XLOOKUP)) continue;
*/
if(!xctx->hilight_nets || !xctx->wire[i].node ||
!xctx->wire[i].node[0] || !bus_hilight_hash_lookup(xctx->wire[i].node, 0,XLOOKUP)) continue;
}
if(xctx->wire[i].bus){
ov = INT_BUS_WIDTH(xctx->lw)> cadhalfdotsize ? INT_BUS_WIDTH(xctx->lw) : CADHALFDOTSIZE;
if(xctx->wire[i].y1 < xctx->wire[i].y2) { y1 = xctx->wire[i].y1-ov; y2 = xctx->wire[i].y2+ov; }
else { y1 = xctx->wire[i].y1+ov; y2 = xctx->wire[i].y2-ov; }
} else {
ov = cadhalfdotsize;
if(xctx->wire[i].y1 < xctx->wire[i].y2) { y1 = xctx->wire[i].y1-ov; y2 = xctx->wire[i].y2+ov; }
else { y1 = xctx->wire[i].y1+ov; y2 = xctx->wire[i].y2-ov; }
}
rect.x1 = xctx->wire[i].x1-ov;
rect.x2 = xctx->wire[i].x2+ov;
rect.y1 = y1;
rect.y2 = y2;
++count;
updatebbox(count,boundbox,&rect);
}
if(has_x && selected != 2) {
for(i=0;i<xctx->texts; ++i)
{
int no_of_lines;
double longest_line;
if(selected == 1 && !xctx->text[i].sel) continue;
#if HAS_CAIRO==1
customfont = set_text_custom_font(&xctx->text[i]);
#endif
if(text_bbox(get_text_floater(i), xctx->text[i].xscale,
xctx->text[i].yscale,xctx->text[i].rot, xctx->text[i].flip,
xctx->text[i].hcenter, xctx->text[i].vcenter,
xctx->text[i].x0, xctx->text[i].y0,
&rect.x1,&rect.y1, &rect.x2,&rect.y2, &no_of_lines, &longest_line) ) {
++count;
updatebbox(count,boundbox,&rect);
}
#if HAS_CAIRO==1
if(customfont) {
cairo_restore(xctx->cairo_ctx);
}
#endif
}
}
for(i=0;i<xctx->instances; ++i)
{
char *type;
Hilight_hashentry *entry;
if(selected == 1 && !xctx->inst[i].sel) continue;
if(selected == 2) {
int found;
type = (xctx->inst[i].ptr+ xctx->sym)->type;
found = 0;
if( type && IS_LABEL_OR_PIN(type)) {
entry=bus_hilight_hash_lookup(xctx->inst[i].lab, 0, XLOOKUP );
if(entry) found = 1;
}
if(!found && xctx->inst[i].color != -10000 ) {
found = 1;
}
if(!found) continue;
}
/* cpu hog 20171206 */
/* symbol_bbox(i, &xctx->inst[i].x1, &xctx->inst[i].y1, &xctx->inst[i].x2, &xctx->inst[i].y2); */
rect.x1=xctx->inst[i].x1;
rect.y1=xctx->inst[i].y1;
rect.x2=xctx->inst[i].x2;
rect.y2=xctx->inst[i].y2;
++count;
updatebbox(count,boundbox,&rect);
}
}
/* flags: bit0: invoke change_linewidth()/xsetLineattributes, bit1: centered zoom */
void zoom_full(int dr, int sel, int flags, double shrink)
{
xRect boundbox;
double yzoom;
double bboxw, bboxh, schw, schh;
if(flags & 1) {
if(tclgetboolvar("change_lw")) {
xctx->lw = 1.;
}
xctx->areax1 = -2*INT_WIDTH(xctx->lw);
xctx->areay1 = -2*INT_WIDTH(xctx->lw);
xctx->areax2 = xctx->xrect[0].width+2*INT_WIDTH(xctx->lw);
xctx->areay2 = xctx->xrect[0].height+2*INT_WIDTH(xctx->lw);
xctx->areaw = xctx->areax2-xctx->areax1;
xctx->areah = xctx->areay2 - xctx->areay1;
}
calc_drawing_bbox(&boundbox, sel);
dbg(1, "zoom_full: %s, %g %g %g %g\n",
xctx->current_win_path, boundbox.x1, boundbox.y1, boundbox.x2, boundbox.y2);
schw = xctx->areaw-4*INT_WIDTH(xctx->lw);
schh = xctx->areah-4*INT_WIDTH(xctx->lw);
bboxw = boundbox.x2-boundbox.x1;
bboxh = boundbox.y2-boundbox.y1;
xctx->zoom = bboxw / schw;
yzoom = bboxh / schh;
if(yzoom > xctx->zoom) xctx->zoom = yzoom;
xctx->zoom /= shrink;
/* we do this here since change_linewidth may not be called if flags & 1 == 0*/
cadhalfdotsize = CADHALFDOTSIZE + 0.04 * (tclgetdoublevar("cadsnap")-10);
xctx->mooz = 1 / xctx->zoom;
if(flags & 2) {
xctx->xorigin = -boundbox.x1 + (xctx->zoom * schw - bboxw) / 2; /* centered */
xctx->yorigin = -boundbox.y1 + (xctx->zoom * schh - bboxh) / 2; /* centered */
} else {
xctx->xorigin = -boundbox.x1 + (1 - shrink) / 2 * xctx->zoom * schw;
xctx->yorigin = -boundbox.y1 + xctx->zoom * schh - bboxh - (1 - shrink) / 2 * xctx->zoom * schh;
}
dbg(1, "zoom_full(): dr=%d sel=%d flags=%d areaw=%d, areah=%d\n", sel, dr, flags, xctx->areaw, xctx->areah);
if(flags & 1) change_linewidth(-1.);
if(dr && has_x) {
draw();
redraw_w_a_l_r_p_rubbers();
}
}
void view_zoom(double z)
{
double factor;
/* int i; */
factor = z!=0.0 ? z : CADZOOMSTEP;
if(xctx->zoom<CADMINZOOM) return;
xctx->zoom/= factor;
xctx->mooz=1/xctx->zoom;
xctx->xorigin=-xctx->mousex_snap+(xctx->mousex_snap+xctx->xorigin)/factor;
xctx->yorigin=-xctx->mousey_snap+(xctx->mousey_snap+xctx->yorigin)/factor;
change_linewidth(-1.);
draw();
redraw_w_a_l_r_p_rubbers();
}
void view_unzoom(double z)
{
double factor;
/* int i; */
factor = z!=0.0 ? z : CADZOOMSTEP;
if(xctx->zoom>CADMAXZOOM) return;
xctx->zoom*= factor;
xctx->mooz=1/xctx->zoom;
/* 20181022 make unzoom and zoom symmetric */
/* keeping the mouse pointer as the origin */
if(tclgetboolvar("unzoom_nodrift")) {
xctx->xorigin=-xctx->mousex_snap+(xctx->mousex_snap+xctx->xorigin)*factor;
xctx->yorigin=-xctx->mousey_snap+(xctx->mousey_snap+xctx->yorigin)*factor;
} else {
xctx->xorigin=xctx->xorigin+xctx->areaw*xctx->zoom*(1-1/factor)/2;
xctx->yorigin=xctx->yorigin+xctx->areah*xctx->zoom*(1-1/factor)/2;
}
change_linewidth(-1.);
draw();
redraw_w_a_l_r_p_rubbers();
}
void set_viewport_size(int w, int h, double lw)
{
xctx->xrect[0].x = 0;
xctx->xrect[0].y = 0;
xctx->xrect[0].width = (unsigned short)w;
xctx->xrect[0].height = (unsigned short)h;
xctx->areax2 = w+2*INT_WIDTH(lw);
xctx->areay2 = h+2*INT_WIDTH(lw);
xctx->areax1 = -2*INT_WIDTH(lw);
xctx->areay1 = -2*INT_WIDTH(lw);
xctx->lw = lw;
xctx->areaw = xctx->areax2-xctx->areax1;
xctx->areah = xctx->areay2-xctx->areay1;
}
void save_restore_zoom(int save)
{
static int savew, saveh; /* safe to keep even with multiple schematics */
static double savexor, saveyor, savezoom, savelw; /* safe to keep even with multiple schematics */
if(save) {
savew = xctx->xrect[0].width;
saveh = xctx->xrect[0].height;
savelw = xctx->lw;
savexor = xctx->xorigin;
saveyor = xctx->yorigin;
savezoom = xctx->zoom;
} else {
xctx->xrect[0].x = 0;
xctx->xrect[0].y = 0;
xctx->xrect[0].width = (unsigned short)savew;
xctx->xrect[0].height = (unsigned short)saveh;
xctx->areax2 = savew+2*INT_WIDTH(savelw);
xctx->areay2 = saveh+2*INT_WIDTH(savelw);
xctx->areax1 = -2*INT_WIDTH(savelw);
xctx->areay1 = -2*INT_WIDTH(savelw);
xctx->lw = savelw;
xctx->areaw = xctx->areax2-xctx->areax1;
xctx->areah = xctx->areay2-xctx->areay1;
xctx->xorigin = savexor;
xctx->yorigin = saveyor;
xctx->zoom = savezoom;
xctx->mooz = 1 / savezoom;
}
}
void zoom_box(double x1, double y1, double x2, double y2, double factor)
{
double yy1;
if(factor == 0.) factor = 1.;
RECTORDER(x1,y1,x2,y2);
xctx->xorigin=-x1;xctx->yorigin=-y1;
xctx->zoom=(x2-x1)/(xctx->areaw-4*INT_WIDTH(xctx->lw));
yy1=(y2-y1)/(xctx->areah-4*INT_WIDTH(xctx->lw));
if(yy1>xctx->zoom) xctx->zoom=yy1;
xctx->zoom*= factor;
xctx->mooz=1/xctx->zoom;
xctx->xorigin=xctx->xorigin+xctx->areaw*xctx->zoom*(1-1/factor)/2;
xctx->yorigin=xctx->yorigin+xctx->areah*xctx->zoom*(1-1/factor)/2;
}
void zoom_rectangle(int what)
{
if( (what & START) )
{
xctx->nl_x1=xctx->nl_x2=xctx->mousex_snap;xctx->nl_y1=xctx->nl_y2=xctx->mousey_snap;
xctx->ui_state |= STARTZOOM;
}
if( what & END)
{
xctx->ui_state &= ~STARTZOOM;
RECTORDER(xctx->nl_x1,xctx->nl_y1,xctx->nl_x2,xctx->nl_y2);
drawtemprect(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
xctx->xorigin=-xctx->nl_x1;xctx->yorigin=-xctx->nl_y1;
xctx->zoom=(xctx->nl_x2-xctx->nl_x1)/(xctx->areaw-4*INT_WIDTH(xctx->lw));
xctx->nl_yy1=(xctx->nl_y2-xctx->nl_y1)/(xctx->areah-4*INT_WIDTH(xctx->lw));
if(xctx->nl_yy1>xctx->zoom) xctx->zoom=xctx->nl_yy1;
xctx->mooz=1/xctx->zoom;
change_linewidth(-1.);
draw();
redraw_w_a_l_r_p_rubbers();
dbg(1, "zoom_rectangle(): coord: %.16g %.16g %.16g %.16g zoom=%.16g\n",
xctx->nl_x1,xctx->nl_y1,xctx->mousex_snap, xctx->mousey_snap,xctx->zoom);
}
if(what & RUBBER)
{
xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1;xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtemprect(xctx->gctiled,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
xctx->nl_x2=xctx->mousex_snap;xctx->nl_y2=xctx->mousey_snap;
/* 20171211 update selected objects while dragging */
rebuild_selected_array();
bbox(START,0.0, 0.0, 0.0, 0.0);
bbox(ADD, xctx->nl_xx1, xctx->nl_yy1, xctx->nl_xx2, xctx->nl_yy2);
bbox(SET,0.0, 0.0, 0.0, 0.0);
draw_selection(xctx->gc[SELLAYER], 0);
bbox(END,0.0, 0.0, 0.0, 0.0);
xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1;xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtemprect(xctx->gc[SELLAYER], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
}
#define STORE
void draw_stuff(void)
{
double x1,y1,w,h, x2, y2;
int i;
int n = 200000;
clear_drawing();
view_unzoom(40);
#ifndef STORE
n /= (cadlayers - 4);
for(xctx->rectcolor = 4; xctx->rectcolor < cadlayers; xctx->rectcolor++) {
#else
#endif
for(i = 0; i < n; ++i)
{
w=(xctx->areaw*xctx->zoom/800) * rand() / (RAND_MAX+1.0);
h=(xctx->areah*xctx->zoom/80) * rand() / (RAND_MAX+1.0);
x1=(xctx->areaw*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->xorigin;
y1=(xctx->areah*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->yorigin;
x2=x1+w;
y2=y1+h;
ORDER(x1,y1,x2,y2);
#ifdef STORE
xctx->rectcolor = (int) (16.0*rand()/(RAND_MAX+1.0))+4;
storeobject(-1, x1, y1, x2, y2, xRECT,xctx->rectcolor, 0, NULL);
#else
drawtemprect(xctx->gc[xctx->rectcolor], ADD, x1, y1, x2, y2);
#endif
}
for(i = 0; i < n; ++i)
{
w=(xctx->areaw*xctx->zoom/80) * rand() / (RAND_MAX+1.0);
h=(xctx->areah*xctx->zoom/800) * rand() / (RAND_MAX+1.0);
x1=(xctx->areaw*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->xorigin;
y1=(xctx->areah*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->yorigin;
x2=x1+w;
y2=y1+h;
ORDER(x1,y1,x2,y2);
#ifdef STORE
xctx->rectcolor = (int) (16.0*rand()/(RAND_MAX+1.0))+4;
storeobject(-1, x1, y1, x2, y2,xRECT,xctx->rectcolor, 0, NULL);
#else
drawtemprect(xctx->gc[xctx->rectcolor], ADD, x1, y1, x2, y2);
#endif
}
for(i = 0; i < n; ++i)
{
w=xctx->zoom * rand() / (RAND_MAX+1.0);
h=w;
x1=(xctx->areaw*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->xorigin;
y1=(xctx->areah*xctx->zoom) * rand() / (RAND_MAX+1.0)-xctx->yorigin;
x2=x1+w;
y2=y1+h;
RECTORDER(x1,y1,x2,y2);
#ifdef STORE
xctx->rectcolor = (int) (16.0*rand()/(RAND_MAX+1.0))+4;
storeobject(-1, x1, y1, x2, y2,xRECT,xctx->rectcolor, 0, NULL);
#else
drawtemprect(xctx->gc[xctx->rectcolor], ADD, x1, y1, x2, y2);
#endif
}
#ifndef STORE
drawtemprect(xctx->gc[xctx->rectcolor], END, 0.0, 0.0, 0.0, 0.0);
}
#else
draw();
#endif
}
static void restore_selection(double x1, double y1, double x2, double y2)
{
double xx1,yy1,xx2,yy2;
xx1 = x1; yy1 = y1; xx2 = x2; yy2 = y2;
RECTORDER(xx1,yy1,xx2,yy2);
rebuild_selected_array();
if(!xctx->lastsel) return;
bbox(START,0.0, 0.0, 0.0, 0.0);
bbox(ADD, xx1, yy1, xx2, yy2);
bbox(SET,0.0, 0.0, 0.0, 0.0);
draw_selection(xctx->gc[SELLAYER], 0);
bbox(END,0.0, 0.0, 0.0, 0.0);
}
void new_wire(int what, double mx_snap, double my_snap)
{
int s_pnetname, modified = 0;
if( (what & PLACE) ) {
s_pnetname = tclgetboolvar("show_pin_net_names");
if( (xctx->ui_state & STARTWIRE) && (xctx->nl_x1!=xctx->nl_x2 || xctx->nl_y1!=xctx->nl_y2) ) {
xctx->push_undo();
if(xctx->manhattan_lines==1) {
if(xctx->nl_xx2!=xctx->nl_xx1) {
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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1,WIRE,0,0,NULL);
modified = 1;
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->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);
storeobject(-1, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL);
modified = 1;
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->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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2,WIRE,0,0,NULL);
modified = 1;
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->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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL);
modified = 1;
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->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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,WIRE,0,0,NULL);
modified = 1;
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();
if(s_pnetname || xctx->hilight_nets) {
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();
} else update_conn_cues(WIRELAYER, 1,1);
/* draw_hilight_net(1);*/ /* for updating connection bubbles on hilight nets */
}
if(! (what &END)) {
xctx->nl_x1=mx_snap;
xctx->nl_y1=my_snap;
xctx->nl_x2=xctx->mousex_snap;
xctx->nl_y2=xctx->mousey_snap;
xctx->nl_xx1=xctx->nl_x1;
xctx->nl_yy1=xctx->nl_y1;
xctx->nl_xx2=xctx->mousex_snap;
xctx->nl_yy2=xctx->mousey_snap;
if(xctx->manhattan_lines==1) {
xctx->nl_x2 = mx_snap; xctx->nl_y2 = my_snap;
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_x2 = mx_snap; xctx->nl_y2 = my_snap;
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_x2 = mx_snap; xctx->nl_y2 = my_snap;
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);
}
}
xctx->ui_state |= STARTWIRE;
if(modified) set_modify(1);
}
if( what & END) {
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);
}
}
}
}
void change_layer()
{
int k, n, type, c;
double x1,y1,x2,y2, a, b, r;
int modified = 0;
if(xctx->lastsel) xctx->push_undo();
for(k=0;k<xctx->lastsel; ++k)
{
n=xctx->sel_array[k].n;
type=xctx->sel_array[k].type;
c=xctx->sel_array[k].col;
if(type==LINE && xctx->line[c][n].sel==SELECTED) {
x1 = xctx->line[c][n].x1;
y1 = xctx->line[c][n].y1;
x2 = xctx->line[c][n].x2;
y2 = xctx->line[c][n].y2;
storeobject(-1, x1,y1,x2,y2,LINE,xctx->rectcolor, 0, xctx->line[c][n].prop_ptr);
modified = 1;
}
if(type==ARC && xctx->arc[c][n].sel==SELECTED) {
x1 = xctx->arc[c][n].x;
y1 = xctx->arc[c][n].y;
r = xctx->arc[c][n].r;
a = xctx->arc[c][n].a;
b = xctx->arc[c][n].b;
store_arc(-1, x1, y1, r, a, b, xctx->rectcolor, 0, xctx->arc[c][n].prop_ptr);
}
if(type==POLYGON && xctx->poly[c][n].sel==SELECTED) {
store_poly(-1, xctx->poly[c][n].x, xctx->poly[c][n].y,
xctx->poly[c][n].points, xctx->rectcolor, 0, xctx->poly[c][n].prop_ptr);
}
else if(type==xRECT && xctx->rect[c][n].sel==SELECTED) {
x1 = xctx->rect[c][n].x1;
y1 = xctx->rect[c][n].y1;
x2 = xctx->rect[c][n].x2;
y2 = xctx->rect[c][n].y2;
storeobject(-1, x1,y1,x2,y2,xRECT,xctx->rectcolor, 0, xctx->rect[c][n].prop_ptr);
modified = 1;
}
else if(type==xTEXT && xctx->text[n].sel==SELECTED) {
if(xctx->rectcolor != xctx->text[n].layer) {
char *p;
my_strdup2(_ALLOC_ID_, &xctx->text[n].prop_ptr,
subst_token(xctx->text[n].prop_ptr, "layer", dtoa(xctx->rectcolor) ));
xctx->text[n].layer = xctx->rectcolor;
p = xctx->text[n].prop_ptr;
while(*p) {
if(*p == '\n') *p = ' ';
++p;
}
modified = 1;
}
}
}
if(xctx->lastsel) delete_only_rect_line_arc_poly();
unselect_all(1);
if(modified) set_modify(1);
}
void new_arc(int what, double sweep)
{
if(what & PLACE) {
xctx->nl_state=0;
xctx->nl_r = -1.;
xctx->nl_sweep_angle=sweep;
xctx->nl_xx1 = xctx->nl_xx2 = xctx->nl_x1 = xctx->nl_x2 = xctx->nl_x3 = xctx->mousex_snap;
xctx->nl_yy1 = xctx->nl_yy2 = xctx->nl_y1 = xctx->nl_y2 = xctx->nl_y3 = xctx->mousey_snap;
xctx->ui_state |= STARTARC;
}
if(what & SET) {
if(xctx->nl_state==0) {
xctx->nl_x2 = xctx->mousex_snap;
xctx->nl_y2 = xctx->mousey_snap;
drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
xctx->nl_state=1;
} else if(xctx->nl_state==1) {
xctx->nl_x3 = xctx->mousex_snap;
xctx->nl_y3 = xctx->mousey_snap;
arc_3_points(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2,
xctx->nl_x3, xctx->nl_y3, &xctx->nl_x, &xctx->nl_y, &xctx->nl_r, &xctx->nl_a, &xctx->nl_b);
if(xctx->nl_sweep_angle==360.) xctx->nl_b=360.;
if(xctx->nl_r>0.) {
xctx->push_undo();
drawarc(xctx->rectcolor, NOW, xctx->nl_x, xctx->nl_y, xctx->nl_r, xctx->nl_a, xctx->nl_b, 0, 0);
store_arc(-1, xctx->nl_x, xctx->nl_y, xctx->nl_r, xctx->nl_a, xctx->nl_b, xctx->rectcolor, 0, NULL);
set_modify(1);
}
xctx->ui_state &= ~STARTARC;
xctx->nl_state=0;
}
}
if(what & RUBBER) {
if(xctx->nl_state==0) {
drawtempline(xctx->gctiled, NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
xctx->nl_xx2 = xctx->mousex_snap;
xctx->nl_yy2 = xctx->mousey_snap;
xctx->nl_xx1 = xctx->nl_x1;xctx->nl_yy1 = xctx->nl_y1;
ORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtempline(xctx->gc[SELLAYER], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
else if(xctx->nl_state==1) {
xctx->nl_x3 = xctx->mousex_snap;
xctx->nl_y3 = xctx->mousey_snap;
if(xctx->nl_r>0.) drawtemparc(xctx->gctiled, NOW, xctx->nl_x, xctx->nl_y, xctx->nl_r, xctx->nl_a, xctx->nl_b);
arc_3_points(xctx->nl_x1, xctx->nl_y1, xctx->nl_x2, xctx->nl_y2,
xctx->nl_x3, xctx->nl_y3, &xctx->nl_x, &xctx->nl_y, &xctx->nl_r, &xctx->nl_a, &xctx->nl_b);
if(xctx->nl_sweep_angle==360.) xctx->nl_b=360.;
if(xctx->nl_r>0.) drawtemparc(xctx->gc[xctx->rectcolor], NOW, xctx->nl_x, xctx->nl_y, xctx->nl_r, xctx->nl_a, xctx->nl_b);
}
}
}
void new_line(int what)
{
int modified = 0;
if( (what & PLACE) )
{
if( (xctx->nl_x1!=xctx->nl_x2 || xctx->nl_y1!=xctx->nl_y2) && (xctx->ui_state & STARTLINE) )
{
xctx->push_undo();
if(xctx->manhattan_lines==1) {
if(xctx->nl_xx2!=xctx->nl_xx1) {
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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy1,LINE,xctx->rectcolor,0,NULL);
modified = 1;
drawline(xctx->rectcolor,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->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);
storeobject(-1, xctx->nl_xx2,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,LINE,xctx->rectcolor,0,NULL);
modified = 1;
drawline(xctx->rectcolor,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->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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx1,xctx->nl_yy2,LINE,xctx->rectcolor,0,NULL);
modified = 1;
drawline(xctx->rectcolor,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->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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2,LINE,xctx->rectcolor,0,NULL);
modified = 1;
drawline(xctx->rectcolor,NOW, xctx->nl_xx1,xctx->nl_yy2,xctx->nl_xx2,xctx->nl_yy2, 0, NULL);
}
} 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);
storeobject(-1, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2,LINE,xctx->rectcolor,0,NULL);
modified = 1;
drawline(xctx->rectcolor,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2, 0, NULL);
}
if(modified) set_modify(1);
}
xctx->nl_x1=xctx->nl_x2=xctx->mousex_snap;xctx->nl_y1=xctx->nl_y2=xctx->mousey_snap;
xctx->ui_state |= STARTLINE;
}
if( what & END)
{
xctx->ui_state &= ~STARTLINE;
}
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 = xctx->mousex_snap; xctx->nl_y2 = xctx->mousey_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[xctx->rectcolor], 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[xctx->rectcolor], 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 = xctx->mousex_snap; xctx->nl_y2 = xctx->mousey_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[xctx->rectcolor], 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[xctx->rectcolor], 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 = xctx->mousex_snap; xctx->nl_y2 = xctx->mousey_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[xctx->rectcolor], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
}
}
}
void new_rect(int what)
{
int modified = 0;
if( (what & PLACE) )
{
if( (xctx->nl_x1!=xctx->nl_x2 || xctx->nl_y1!=xctx->nl_y2) && (xctx->ui_state & STARTRECT) )
{
int save_draw;
RECTORDER(xctx->nl_x1,xctx->nl_y1,xctx->nl_x2,xctx->nl_y2);
xctx->push_undo();
drawrect(xctx->rectcolor, NOW, xctx->nl_x1,xctx->nl_y1,xctx->nl_x2,xctx->nl_y2, 0);
save_draw = xctx->draw_window;
xctx->draw_window = 1;
/* draw fill pattern even in xcopyarea mode */
filledrect(xctx->rectcolor, NOW, xctx->nl_x1,xctx->nl_y1,xctx->nl_x2,xctx->nl_y2);
xctx->draw_window = save_draw;
storeobject(-1, xctx->nl_x1,xctx->nl_y1,xctx->nl_x2,xctx->nl_y2,xRECT,xctx->rectcolor, 0, NULL);
modified = 1;
}
xctx->nl_x1=xctx->nl_x2=xctx->mousex_snap;xctx->nl_y1=xctx->nl_y2=xctx->mousey_snap;
xctx->ui_state |= STARTRECT;
if(modified) set_modify(1);
}
if( what & END)
{
xctx->ui_state &= ~STARTRECT;
}
if(what & RUBBER)
{
xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1;xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtemprect(xctx->gctiled,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
xctx->nl_x2=xctx->mousex_snap;xctx->nl_y2=xctx->mousey_snap;
xctx->nl_xx1=xctx->nl_x1;xctx->nl_yy1=xctx->nl_y1;xctx->nl_xx2=xctx->nl_x2;xctx->nl_yy2=xctx->nl_y2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtemprect(xctx->gc[xctx->rectcolor], NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
}
void new_polygon(int what)
{
if( what & PLACE ) xctx->nl_points=0; /* start new polygon placement */
if(xctx->nl_points >= xctx->nl_maxpoints-1) { /* check storage for 2 xctx->nl_points */
xctx->nl_maxpoints = (1+xctx->nl_points / CADCHUNKALLOC) * CADCHUNKALLOC;
my_realloc(_ALLOC_ID_, &xctx->nl_polyx, sizeof(double)*xctx->nl_maxpoints);
my_realloc(_ALLOC_ID_, &xctx->nl_polyy, sizeof(double)*xctx->nl_maxpoints);
}
if( what & PLACE )
{
/* fprintf(errfp, "new_poly: PLACE, nl_points=%d\n", xctx->nl_points); */
xctx->nl_polyy[xctx->nl_points]=xctx->mousey_snap;
xctx->nl_polyx[xctx->nl_points]=xctx->mousex_snap;
xctx->nl_points++;
xctx->nl_polyx[xctx->nl_points]=xctx->nl_polyx[xctx->nl_points-1]; /* prepare next point for rubber */
xctx->nl_polyy[xctx->nl_points] = xctx->nl_polyy[xctx->nl_points-1];
/* fprintf(errfp, "added point: %.16g %.16g\n", xctx->nl_polyx[xctx->nl_points-1],
xctx->nl_polyy[xctx->nl_points-1]); */
xctx->ui_state |= STARTPOLYGON;
set_modify(1);
}
if( what & ADD)
{
/* closed poly */
if(what & END) {
/* delete last rubber */
drawtemppolygon(xctx->gctiled, NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points+1);
xctx->nl_polyx[xctx->nl_points] = xctx->nl_polyx[0];
xctx->nl_polyy[xctx->nl_points] = xctx->nl_polyy[0];
/* add point */
} else if(xctx->nl_polyx[xctx->nl_points] != xctx->nl_polyx[xctx->nl_points-1] ||
xctx->nl_polyy[xctx->nl_points] != xctx->nl_polyy[xctx->nl_points-1]) {
xctx->nl_polyx[xctx->nl_points] = xctx->mousex_snap;
xctx->nl_polyy[xctx->nl_points] = xctx->mousey_snap;
} else {
return;
}
xctx->nl_points++;
/* prepare next point for rubber */
xctx->nl_polyx[xctx->nl_points]=xctx->nl_polyx[xctx->nl_points-1];
xctx->nl_polyy[xctx->nl_points]=xctx->nl_polyy[xctx->nl_points-1];
}
/* end open or closed poly by user request */
if((what & SET || (what & END)) ||
/* closed poly end by clicking on first point */
((what & ADD) && xctx->nl_polyx[xctx->nl_points-1] == xctx->nl_polyx[0] &&
xctx->nl_polyy[xctx->nl_points-1] == xctx->nl_polyy[0]) ) {
xctx->push_undo();
drawtemppolygon(xctx->gctiled, NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points+1);
store_poly(-1, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points, xctx->rectcolor, 0, NULL);
/* fprintf(errfp, "new_poly: finish: nl_points=%d\n", xctx->nl_points); */
drawtemppolygon(xctx->gc[xctx->rectcolor], NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points);
xctx->ui_state &= ~STARTPOLYGON;
drawpolygon(xctx->rectcolor, NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points, 0, 0);
my_free(_ALLOC_ID_, &xctx->nl_polyx);
my_free(_ALLOC_ID_, &xctx->nl_polyy);
xctx->nl_maxpoints = xctx->nl_points = 0;
}
if(what & RUBBER)
{
/* fprintf(errfp, "new_poly: RUBBER\n"); */
drawtemppolygon(xctx->gctiled, NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points+1);
xctx->nl_polyy[xctx->nl_points] = xctx->mousey_snap;
xctx->nl_polyx[xctx->nl_points] = xctx->mousex_snap;
drawtemppolygon(xctx->gc[xctx->rectcolor], NOW, xctx->nl_polyx, xctx->nl_polyy, xctx->nl_points+1);
}
}
#if HAS_CAIRO==1
int text_bbox(const char *str, double xscale, double yscale,
short rot, short flip, int hcenter, int vcenter, double x1,double y1, double *rx1, double *ry1,
double *rx2, double *ry2, int *cairo_lines, double *cairo_longest_line)
{
int c=0;
char *str_ptr, *s = NULL;
double size;
cairo_text_extents_t ext;
cairo_font_extents_t fext;
double ww, hh, maxw;
/* will not match exactly font metrics when doing ps/svg output , but better than nothing */
if(!has_x) return text_bbox_nocairo(str, xscale, yscale, rot, flip, hcenter, vcenter, x1, y1,
rx1, ry1, rx2, ry2, cairo_lines, cairo_longest_line);
size = xscale*52.*cairo_font_scale;
/* if(size*xctx->mooz>800.) { */
/* return 0; */
/* } */
cairo_set_font_size (xctx->cairo_ctx, size*xctx->mooz);
cairo_font_extents(xctx->cairo_ctx, &fext);
ww=0.; hh=1.;
c=0;
*cairo_lines=1;
my_strdup2(_ALLOC_ID_, &s, str);
str_ptr = s;
while( s && s[c] ) {
if(s[c] == '\n') {
s[c]='\0';
++hh;
(*cairo_lines)++;
if(str_ptr[0]!='\0') {
cairo_text_extents(xctx->cairo_ctx, str_ptr, &ext);
maxw = ext.x_advance > ext.width ? ext.x_advance : ext.width;
if(maxw > ww) ww= maxw;
}
s[c]='\n';
str_ptr = s+c+1;
} else {
}
++c;
}
if(str_ptr && str_ptr[0]!='\0') {
cairo_text_extents(xctx->cairo_ctx, str_ptr, &ext);
maxw = ext.x_advance > ext.width ? ext.x_advance : ext.width;
if(maxw > ww) ww= maxw;
}
my_free(_ALLOC_ID_, &s);
hh = hh*fext.height * cairo_font_line_spacing;
*cairo_longest_line = ww;
*rx1=x1;*ry1=y1;
if(hcenter) {
if (rot==0 && flip == 0) { *rx1-= ww*xctx->zoom/2;}
else if(rot==1 && flip == 0) { *ry1-= ww*xctx->zoom/2;}
else if(rot==2 && flip == 0) { *rx1+= ww*xctx->zoom/2;}
else if(rot==3 && flip == 0) { *ry1+= ww*xctx->zoom/2;}
else if(rot==0 && flip == 1) { *rx1+= ww*xctx->zoom/2;}
else if(rot==1 && flip == 1) { *ry1+= ww*xctx->zoom/2;}
else if(rot==2 && flip == 1) { *rx1-= ww*xctx->zoom/2;}
else if(rot==3 && flip == 1) { *ry1-= ww*xctx->zoom/2;}
}
if(vcenter) {
if (rot==0 && flip == 0) { *ry1-= hh*xctx->zoom/2;}
else if(rot==1 && flip == 0) { *rx1+= hh*xctx->zoom/2;}
else if(rot==2 && flip == 0) { *ry1+= hh*xctx->zoom/2;}
else if(rot==3 && flip == 0) { *rx1-= hh*xctx->zoom/2;}
else if(rot==0 && flip == 1) { *ry1-= hh*xctx->zoom/2;}
else if(rot==1 && flip == 1) { *rx1+= hh*xctx->zoom/2;}
else if(rot==2 && flip == 1) { *ry1+= hh*xctx->zoom/2;}
else if(rot==3 && flip == 1) { *rx1-= hh*xctx->zoom/2;}
}
ROTATION(rot, flip, 0.0,0.0, ww*xctx->zoom,hh*xctx->zoom,(*rx2),(*ry2));
*rx2+=*rx1;*ry2+=*ry1;
if (rot==0) {*ry1-=cairo_vert_correct; *ry2-=cairo_vert_correct;}
else if(rot==1) {*rx1+=cairo_vert_correct; *rx2+=cairo_vert_correct;}
else if(rot==2) {*ry1+=cairo_vert_correct; *ry2+=cairo_vert_correct;}
else if(rot==3) {*rx1-=cairo_vert_correct; *rx2-=cairo_vert_correct;}
RECTORDER((*rx1),(*ry1),(*rx2),(*ry2));
return 1;
}
int text_bbox_nocairo(const char *str,double xscale, double yscale,
short rot, short flip, int hcenter, int vcenter, double x1,double y1, double *rx1, double *ry1,
double *rx2, double *ry2, int *cairo_lines, double *cairo_longest_line)
#else
int text_bbox(const char *str,double xscale, double yscale,
short rot, short flip, int hcenter, int vcenter, double x1,double y1, double *rx1, double *ry1,
double *rx2, double *ry2, int *cairo_lines, double *cairo_longest_line)
#endif
{
register int c=0, length =0;
double w, h;
w=0;h=1;
*cairo_lines = 1;
if(str!=NULL) while( str[c] )
{
if((str)[c++]=='\n') {(*cairo_lines)++; h++; length=0;}
else length++;
if(length > w)
w = length;
}
w *= (FONTWIDTH+FONTWHITESPACE)*xscale* tclgetdoublevar("nocairo_font_xscale");
*cairo_longest_line = w;
h *= (FONTHEIGHT+FONTDESCENT+FONTWHITESPACE)*yscale* tclgetdoublevar("nocairo_font_yscale");
*rx1=x1;*ry1=y1;
if( rot==0) *ry1-=nocairo_vert_correct;
else if(rot==1) *rx1+=nocairo_vert_correct;
else if(rot==2) *ry1+=nocairo_vert_correct;
else *rx1-=nocairo_vert_correct;
if(hcenter) {
if (rot==0 && flip == 0) { *rx1-= w/2;}
else if(rot==1 && flip == 0) { *ry1-= w/2;}
else if(rot==2 && flip == 0) { *rx1+= w/2;}
else if(rot==3 && flip == 0) { *ry1+= w/2;}
else if(rot==0 && flip == 1) { *rx1+= w/2;}
else if(rot==1 && flip == 1) { *ry1+= w/2;}
else if(rot==2 && flip == 1) { *rx1-= w/2;}
else if(rot==3 && flip == 1) { *ry1-= w/2;}
}
if(vcenter) {
if (rot==0 && flip == 0) { *ry1-= h/2;}
else if(rot==1 && flip == 0) { *rx1+= h/2;}
else if(rot==2 && flip == 0) { *ry1+= h/2;}
else if(rot==3 && flip == 0) { *rx1-= h/2;}
else if(rot==0 && flip == 1) { *ry1-= h/2;}
else if(rot==1 && flip == 1) { *rx1+= h/2;}
else if(rot==2 && flip == 1) { *ry1+= h/2;}
else if(rot==3 && flip == 1) { *rx1-= h/2;}
}
ROTATION(rot, flip, 0.0,0.0,w,h,(*rx2),(*ry2));
*rx2+=*rx1;*ry2+=*ry1;
RECTORDER((*rx1),(*ry1),(*rx2),(*ry2));
return 1;
}
/* round() does not exist in C89 */
double my_round(double a)
{
/* return 0.0 or -0.0 if a == 0.0 or -0.0 */
return (a > 0.0) ? floor(a + 0.5) : (a < 0.0) ? ceil(a - 0.5) : a;
}
double round_to_n_digits(double x, int n)
{
double scale;
if(x == 0.0) return x;
scale = pow(10.0, ceil(log10(fabs(x))) - n);
return my_round(x / scale) * scale;
}
double floor_to_n_digits(double x, int n)
{
double scale;
if(x == 0.0) return x;
scale = pow(10.0, ceil(log10(fabs(x))) - n);
return floor(x / scale) * scale;
}
double ceil_to_n_digits(double x, int n)
{
double scale;
if(x == 0.0) return x;
scale = pow(10.0, ceil(log10(fabs(x))) - n);
return ceil(x / scale) * scale;
}
int create_text(int draw_text, double x, double y, int rot, int flip, const char *txt,
const char *props, double hsize, double vsize)
{
int textlayer;
xText *t;
int save_draw;
#if HAS_CAIRO==1
const char *textfont;
#endif
check_text_storage();
t = &xctx->text[xctx->texts];
t->txt_ptr=NULL;
t->prop_ptr=NULL; /* 20111006 added missing initialization of pointer */
t->floater_ptr = NULL;
t->font=NULL;
t->floater_instname=NULL;
my_strdup2(_ALLOC_ID_, &t->txt_ptr, txt);
t->x0=x;
t->y0=y;
t->rot=(short int) rot;
t->flip=(short int) flip;
t->sel=0;
t->xscale= hsize;
t->yscale= vsize;
my_strdup(_ALLOC_ID_, &t->prop_ptr, props);
/* debug ... */
/* t->prop_ptr=NULL; */
dbg(1, "place_text(): done text input\n");
set_text_flags(t);
textlayer = t->layer;
if(textlayer < 0 || textlayer >= cadlayers) textlayer = TEXTLAYER;
if(draw_text) {
#if HAS_CAIRO==1
textfont = t->font;
if((textfont && textfont[0]) || (t->flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
cairo_font_slant_t slant;
cairo_font_weight_t weight;
textfont = (t->font && t->font[0]) ? t->font : tclgetvar("cairo_font_name");
weight = ( t->flags & TEXT_BOLD) ? CAIRO_FONT_WEIGHT_BOLD : CAIRO_FONT_WEIGHT_NORMAL;
slant = CAIRO_FONT_SLANT_NORMAL;
if(t->flags & TEXT_ITALIC) slant = CAIRO_FONT_SLANT_ITALIC;
if(t->flags & TEXT_OBLIQUE) slant = CAIRO_FONT_SLANT_OBLIQUE;
cairo_save(xctx->cairo_ctx);
cairo_save(xctx->cairo_save_ctx);
xctx->cairo_font =
cairo_toy_font_face_create(textfont, slant, weight);
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
save_draw=xctx->draw_window;
xctx->draw_window=1;
draw_string(textlayer, NOW, get_text_floater(xctx->texts), t->rot, t->flip,
t->hcenter, t->vcenter, t->x0,t->y0, t->xscale, t->yscale);
xctx->draw_window = save_draw;
#if HAS_CAIRO==1
if((textfont && textfont[0]) || (t->flags & (TEXT_BOLD | TEXT_OBLIQUE | TEXT_ITALIC))) {
cairo_restore(xctx->cairo_ctx);
cairo_restore(xctx->cairo_save_ctx);
}
#endif
}
xctx->texts++;
return 1;
}
int place_text(int draw_text, double mx, double my)
{
char *txt, *props, *hsize, *vsize;
tclsetvar("props","");
tclsetvar("retval","");
if(!tclgetvar("hsize"))
tclsetvar("hsize","0.4");
if(!tclgetvar("vsize"))
tclsetvar("vsize","0.4");
xctx->semaphore++;
tcleval("enter_text {text:} normal");
xctx->semaphore--;
dbg(1, "place_text(): hsize=%s vsize=%s\n",tclgetvar("hsize"), tclgetvar("vsize") );
/* get: retval, hsize, vsize, props, */
txt = (char *)tclgetvar("retval");
props = (char *)tclgetvar("props");
hsize = (char *)tclgetvar("hsize");
vsize = (char *)tclgetvar("vsize");
if(!txt || !strcmp(txt,"")) return 0; /* dont allocate text object if empty string given */
xctx->push_undo();
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);
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);
return 1;
}
void pan(int what, int mx, int my)
{
int dx, dy, ddx, ddy;
if(what & START) {
xctx->mmx_s = xctx->mx_s = mx;
xctx->mmy_s = xctx->my_s = my;
xctx->xorig_save = xctx->xorigin;
xctx->yorig_save = xctx->yorigin;
}
else if(what == RUBBER) {
dx = mx - xctx->mx_s;
dy = my - xctx->my_s;
ddx = abs(mx -xctx->mmx_s);
ddy = abs(my -xctx->mmy_s);
if(ddx>5 || ddy>5) {
xctx->xorigin = xctx->xorig_save + dx*xctx->zoom;
xctx->yorigin = xctx->yorig_save + dy*xctx->zoom;
draw();
xctx->mmx_s = mx;
xctx->mmy_s = my;
}
}
}
/* instead of doing a drawtemprect(xctx->gctiled, NOW, ....) do 4
* XCopy Area operations */
void fix_restore_rect(double x1, double y1, double x2, double y2)
{
/* horizontal lines */
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
x1, y1, x2, y1, x1, y1,
xctx->lw);
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
x1, y2, x2, y2, x1, y2,
xctx->lw);
/* vertical lines */
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
x1, y1, x1, y2, x1, y1,
xctx->lw);
MyXCopyAreaDouble(display, xctx->save_pixmap, xctx->window, xctx->gc[0],
x2, y1, x2, y2, x2, y1,
xctx->lw);
}
/* 20150927 select=1: select objects, select=0: unselect objects */
void select_rect(int what, int select)
{
dbg(1, "select_rect(): what=%d, mousex_save=%g mousey_save=%g, mousex_snap=%g mousey_snap=%g\n",
what, xctx->mx_double_save, xctx->my_double_save, xctx->mousex_snap, xctx->mousey_snap);
if(what & RUBBER)
{
if(xctx->nl_sem==0) {
fprintf(errfp, "ERROR: select_rect() RUBBER called before START\n");
tcleval("alert_ {ERROR: select_rect() RUBBER called before START} {}");
}
xctx->nl_xx1=xctx->nl_xr;xctx->nl_xx2=xctx->nl_xr2;xctx->nl_yy1=xctx->nl_yr;xctx->nl_yy2=xctx->nl_yr2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
if(fix_broken_tiled_fill || !_unix) {
fix_restore_rect(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
} else {
drawtemprect(xctx->gctiled,NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
xctx->nl_xr2=xctx->mousex_snap;xctx->nl_yr2=xctx->mousey_snap;
/* 20171026 update unselected objects while dragging */
rebuild_selected_array();
#if 0
if(xctx->nl_dir == 0) {
bbox(START,0.0, 0.0, 0.0, 0.0);
bbox(ADD, xctx->nl_xx1, xctx->nl_yy1, xctx->nl_xx2, xctx->nl_yy2);
bbox(SET,0.0, 0.0, 0.0, 0.0);
}
#endif
draw_selection(xctx->gc[SELLAYER], 0);
/* if(xctx->nl_sel) { */
if(xctx->nl_dir == 0) select_inside(xctx->nl_xx1, xctx->nl_yy1, xctx->nl_xx2, xctx->nl_yy2, xctx->nl_sel);
else select_touch(xctx->nl_xx1, xctx->nl_yy1, xctx->nl_xx2, xctx->nl_yy2, xctx->nl_sel);
/* } */
#if 0
if(xctx->nl_dir == 0) {
bbox(END,0.0, 0.0, 0.0, 0.0);
}
#endif
xctx->nl_xx1=xctx->nl_xr;xctx->nl_xx2=xctx->nl_xr2;xctx->nl_yy1=xctx->nl_yr;xctx->nl_yy2=xctx->nl_yr2;
RECTORDER(xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
drawtemprect(xctx->gc[SELLAYER],NOW, xctx->nl_xx1,xctx->nl_yy1,xctx->nl_xx2,xctx->nl_yy2);
}
else if(what & START)
{
/*
* if(xctx->nl_sem==1) {
* fprintf(errfp, "ERROR: reentrant call of select_rect()\n");
* tcleval("alert_ {ERROR: reentrant call of select_rect()} {}");
* }
*/
xctx->nl_sel = select;
xctx->ui_state |= STARTSELECT;
/* use m[xy]_double_save instead of mouse[xy]_snap */
/* to avoid delays in setting the start point of a */
/* selection rectangle, this is noticeable and annoying on */
/* networked / slow X servers. 20171218 */
/* xctx->nl_xr=xctx->nl_xr2=xctx->mousex_snap; */
/* xctx->nl_yr=xctx->nl_yr2=xctx->mousey_snap; */
xctx->nl_xr=xctx->nl_xr2=xctx->mx_double_save;
xctx->nl_yr=xctx->nl_yr2=xctx->my_double_save;
xctx->nl_sem=1;
}
else if(what & END)
{
RECTORDER(xctx->nl_xr,xctx->nl_yr,xctx->nl_xr2,xctx->nl_yr2);
if(fix_broken_tiled_fill || !_unix) {
fix_restore_rect(xctx->nl_xr, xctx->nl_yr, xctx->nl_xr2, xctx->nl_yr2);
} else {
drawtemprect(xctx->gctiled, NOW, xctx->nl_xr,xctx->nl_yr,xctx->nl_xr2,xctx->nl_yr2);
}
/* draw_selection(xctx->gc[SELLAYER], 0); */
if(xctx->nl_dir == 0) select_inside(xctx->nl_xr,xctx->nl_yr,xctx->nl_xr2,xctx->nl_yr2, xctx->nl_sel);
else select_touch(xctx->nl_xr,xctx->nl_yr,xctx->nl_xr2,xctx->nl_yr2, xctx->nl_sel);
#if 0
if(xctx->nl_dir == 0) {
bbox(START,0.0, 0.0, 0.0, 0.0);
bbox(ADD, xctx->nl_xr, xctx->nl_yr, xctx->nl_xr2, xctx->nl_yr2);
bbox(SET,0.0, 0.0, 0.0, 0.0);
}
#endif
draw_selection(xctx->gc[SELLAYER], 0);
#if 0
if(xctx->nl_dir == 0) {
bbox(END,0.0, 0.0, 0.0, 0.0);
}
#endif
/* /20171219 */
xctx->ui_state &= ~STARTSELECT;
xctx->nl_sem=0;
}
}