1 /*----------------------------------------------------------------------*/
2 /* xcircuit.c --- An X-windows program for drawing circuit diagrams */
3 /* Copyright (c) 2002 R. Timothy Edwards */
4 /* */
5 /* This program is free software; you can redistribute it and/or modify */
6 /* it under the terms of the GNU General Public License as published by */
7 /* the Free Software Foundation; either version 2 of the License, or */
8 /* (at your option) any later version. */
9 /* */
10 /* This program is distributed in the hope that it will be useful, */
11 /* but WITHOUT ANY WARRANTY; without even the implied warranty of */
12 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */
13 /* GNU General Public License for more details. */
14 /* */
15 /* You should have received a copy of the GNU General Public License */
16 /* along with this program; if not, write to: */
17 /* Free Software Foundation, Inc. */
18 /* 59 Temple Place, Suite 330 */
19 /* Boston, MA 02111-1307 USA */
20 /* */
21 /* See file ./README for contact information */
22 /*----------------------------------------------------------------------*/
23
24 /*----------------------------------------------------------------------*/
25 /* Uses the Xw widget set (see directory Xw) */
26 /* Xcircuit written by Tim Edwards beginning 8/13/93 */
27 /*----------------------------------------------------------------------*/
28
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <stdlib.h>
32 #include <string.h>
33 #include <signal.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <errno.h>
37 #include <limits.h>
38 #include <locale.h>
39 #include <ctype.h>
40 #ifndef XC_WIN32
41 #include <unistd.h> /* for unlink() */
42
43 #include <X11/Xresource.h>
44 #include <X11/Intrinsic.h>
45 #include <X11/StringDefs.h>
46 #include <X11/Shell.h>
47 #include <X11/Xutil.h>
48 #include <X11/cursorfont.h>
49 #include <X11/Xproto.h>
50 #include <X11/Xatom.h>
51 #endif
52
53 #ifdef TCL_WRAPPER
54 #include <tk.h>
55 #else
56 #ifndef XC_WIN32
57 #include "Xw/Xw.h"
58 #include "Xw/Form.h"
59 #include "Xw/WorkSpace.h"
60 #include "Xw/PButton.h"
61 #include "Xw/SText.h"
62 #include "Xw/Cascade.h"
63 #include "Xw/PopupMgr.h"
64 #include "Xw/MenuBtn.h"
65 #include "Xw/BBoard.h"
66 #include "Xw/TextEdit.h"
67 #include "Xw/Toggle.h"
68 #endif
69 #endif
70
71 #if defined(XC_WIN32) && defined(TCL_WRAPPER)
72 #include <X11/Xlib.h>
73 #include <X11/Xutil.h>
74 #include <X11/cursorfont.h>
75 #include <X11/Xatom.h>
76 #endif
77
78 #ifndef P_tmpdir
79 #define P_tmpdir TMPDIR
80 #endif
81
82 /*----------------------------------------------------------------------*/
83 /* Local includes */
84 /*----------------------------------------------------------------------*/
85
86 #include "xcircuit.h"
87 #include "cursors.h"
88 #include "colordefs.h"
89 #include "menudep.h"
90
91 /*----------------------------------------------------------------------*/
92 /* Function prototype declarations */
93 /*----------------------------------------------------------------------*/
94 #include "prototypes.h"
95
96 /*----------------------------------------------------------------------*/
97 /* Global Variable definitions */
98 /*----------------------------------------------------------------------*/
99
100 char _STR2[250]; /* Specifically for text returned from the popup prompt */
101 char _STR[150]; /* Generic multipurpose string */
102 int pressmode; /* Whether we are in a press & hold state */
103 Display *dpy = NULL; /* Works well to make this globally accessible */
104 Colormap cmap;
105 Pixmap STIPPLE[STIPPLES]; /* Polygon fill-style stipple patterns */
106 Cursor appcursors[NUM_CURSORS];
107 ApplicationData appdata;
108 XCWindowData *areawin;
109 Globaldata xobjs;
110 int number_colors;
111 colorindex *colorlist;
112 short menusize;
113 xcIntervalId printtime_id;
114 short beeper;
115 short fontcount;
116 int screenDPI;
117 fontinfo *fonts;
118 short popups; /* total number of popup widgets on the screen */
119
120 /*----------------------------------------------------------------------*/
121 /* Externally defined variables */
122 /*----------------------------------------------------------------------*/
123
124 extern aliasptr aliastop;
125 extern char version[];
126
127 #ifdef TCL_WRAPPER
128 extern Tcl_Interp *xcinterp;
129 #else
130 extern XtAppContext app;
131 #endif
132
133 #if !defined(HAVE_CAIRO)
134 extern Pixmap dbuf;
135 #endif
136
137 /* Bad hack for problems with the DECstation. . . don't know why */
138 #ifdef UniqueContextProblem
139 #undef XUniqueContext
XUniqueContext()140 XContext XUniqueContext()
141 {
142 return XrmUniqueQuark();
143 }
144 #endif
145 /* End of bad hack. . . */
146
147 /*----------------------------------------------------------------------*/
148 /* Update the list of colors in the colormap */
149 /*----------------------------------------------------------------------*/
150
addtocolorlist(xcWidget button,int cvalue)151 void addtocolorlist(xcWidget button, int cvalue)
152 {
153 number_colors++;
154 colorlist = (colorindex *)realloc(colorlist, number_colors *
155 sizeof(colorindex));
156 colorlist[number_colors - 1].cbutton = button;
157 colorlist[number_colors - 1].color.pixel = cvalue;
158
159 /* Get and store the RGB values of the new color */
160
161 if (areawin && (areawin->area == NULL)) {
162 colorlist[number_colors - 1].color.red = 256 * (cvalue & 0xff);
163 colorlist[number_colors - 1].color.green = 256 * ((cvalue >> 8) & 0xff);
164 colorlist[number_colors - 1].color.blue = 256 * ((cvalue >> 16) & 0xff);
165 }
166 else
167 XQueryColors(dpy, cmap, &(colorlist[number_colors - 1].color), 1);
168 }
169
170 /*----------------------------------------------------------------------*/
171 /* Add a new color button to the color menu */
172 /* called if new color button needs to be made */
173 /*----------------------------------------------------------------------*/
174
175 #ifdef TCL_WRAPPER
176
addnewcolorentry(int ccolor)177 int addnewcolorentry(int ccolor)
178 {
179 xcWidget newbutton = (xcWidget)NULL;
180 int i;
181
182 /* check to see if entry is already in the color list */
183
184 for (i = NUMBER_OF_COLORS; i < number_colors; i++)
185 if (colorlist[i].color.pixel == ccolor) break;
186
187 /* make new entry in the menu */
188
189 if (i == number_colors) {
190 addtocolorlist(newbutton, ccolor);
191 sprintf(_STR2,
192 "xcircuit::newcolorbutton %d %d %d %d",
193 colorlist[i].color.red, colorlist[i].color.green,
194 colorlist[i].color.blue, i);
195 Tcl_Eval(xcinterp, _STR2);
196 }
197 return i;
198 }
199
200 #endif /* TCL_WRAPPER */
201
202 /*---------------*/
203 /* Quit xcircuit */
204 /*---------------*/
205
quit(xcWidget w,caddr_t nulldata)206 void quit(xcWidget w, caddr_t nulldata)
207 {
208 int i;
209 Matrixptr curmatrix, dmatrix;
210
211 /* free up CTM Matrix Stack */
212 if (areawin != NULL) {
213 curmatrix = areawin->MatStack;
214 while (curmatrix != NULL) {
215 dmatrix = curmatrix->nextmatrix;
216 free(curmatrix);
217 curmatrix = dmatrix;
218 }
219 areawin->MatStack = NULL;
220 }
221
222 /* free the colormap if a new one has been installed */
223
224 if (dpy != NULL)
225 if (cmap != DefaultColormap(dpy, DefaultScreen(dpy)))
226 XFreeColormap(dpy, cmap);
227
228 /* exit ghostscript if background rendering was enabled */
229
230 exit_gs();
231
232 #ifdef TCL_WRAPPER
233 /* exit ngspice if the simulator is still running */
234
235 exit_spice();
236 #endif
237
238 /* remove any temporary files created for background rendering */
239
240 for (i = 0; i < xobjs.pages; i++) {
241 if (xobjs.pagelist[i]->pageinst != NULL)
242 if (xobjs.pagelist[i]->background.name != (char *)NULL)
243 if (*(xobjs.pagelist[i]->background.name) == '@')
244 unlink(xobjs.pagelist[i]->background.name + 1);
245 }
246
247 /* remove the temporary file and free the filename memory */
248 /* if w = NULL, quit() was reached from Ctrl-C. Don't */
249 /* remove the temporary file; instead, notify user of the */
250 /* filename. */
251
252 if (xobjs.tempfile != NULL) {
253 if (w != NULL) {
254 #ifndef XC_WIN32
255 if (unlink(xobjs.tempfile) < 0)
256 Fprintf(stderr, "Error %d unlinking file \"%s\"\n", errno, xobjs.tempfile);
257 #else
258 if (DeleteFile(xobjs.tempfile) != TRUE)
259 Fprintf(stderr, "Error %d unlinking file \"%s\"\n",
260 GetLastError(), xobjs.tempfile);
261 #endif
262 }
263 else
264 Fprintf(stderr, "Ctrl-C exit: reload workspace from \"%s\"\n",
265 xobjs.tempfile);
266 free(xobjs.tempfile);
267 xobjs.tempfile = NULL;
268 }
269
270 #if defined(HAVE_PYTHON)
271 /* exit by exiting the Python interpreter, if enabled */
272 exit_interpreter();
273 #elif defined(TCL_WRAPPER)
274 /* Let Tcl/Tk handle exit */
275 return;
276 #elif defined(XC_WIN32)
277 PostQuitMessage(0);
278 #else
279 exit(0);
280 #endif
281
282 }
283
284 /*--------------------------------------------------------------*/
285 /* Check for changes in an object and all of its descendents */
286 /*--------------------------------------------------------------*/
287
getchanges(objectptr thisobj)288 u_short getchanges(objectptr thisobj)
289 {
290 genericptr *pgen;
291 objinstptr pinst;
292 u_short changes = thisobj->changes;
293
294 for (pgen = thisobj->plist; pgen < thisobj->plist + thisobj->parts; pgen++) {
295 if (IS_OBJINST(*pgen)) {
296 pinst = TOOBJINST(pgen);
297 changes += getchanges(pinst->thisobject);
298 }
299 }
300 return changes;
301 }
302
303 /*--------------------------------------------------------------*/
304 /* Check to see if any objects in xcircuit have been modified */
305 /* without saving. */
306 /*--------------------------------------------------------------*/
307
countchanges(char ** promptstr)308 u_short countchanges(char **promptstr)
309 {
310 int slen = 1, i; /* , j; (jdk) */
311 u_short locchanges, changes = 0, words = 1;
312 objectptr thisobj;
313 char *fname;
314 TechPtr ns;
315
316 if (promptstr != NULL) slen += strlen(*promptstr);
317
318 for (i = 0; i < xobjs.pages; i++) {
319 if (xobjs.pagelist[i]->pageinst != NULL) {
320 thisobj = xobjs.pagelist[i]->pageinst->thisobject;
321 locchanges = getchanges(thisobj);
322 if (locchanges > 0) {
323 if (promptstr != NULL) {
324 slen += strlen(thisobj->name) + 2;
325 *promptstr = (char *)realloc(*promptstr, slen);
326 if ((words % 8) == 0) strcat(*promptstr, ",\n");
327 else if (changes > 0) strcat(*promptstr, ", ");
328 strcat(*promptstr, thisobj->name);
329 words++;
330 }
331 changes += locchanges;
332 }
333 }
334 }
335
336 /* Check all library objects for unsaved changes */
337
338 for (ns = xobjs.technologies; ns != NULL; ns = ns->next) {
339 tech_set_changes(ns);
340 if ((ns->flags & TECH_CHANGED) != 0) {
341 changes++;
342 if (promptstr != NULL) {
343 fname = ns->filename;
344 if (fname != NULL) {
345 slen += strlen(fname) + 2;
346 *promptstr = (char *)realloc(*promptstr, slen);
347 if ((words % 8) == 0) strcat(*promptstr, ",\n");
348 else if (changes > 0) strcat(*promptstr, ", ");
349 strcat(*promptstr, fname);
350 words++;
351 }
352 }
353 }
354 }
355 return changes;
356 }
357
358 /*----------------------------------------------*/
359 /* Check for conditions to approve program exit */
360 /* Return 0 if prompt "Okay" button ends the */
361 /* program, 1 if the program should exit */
362 /* immediately. */
363 /*----------------------------------------------*/
364
365 #ifdef TCL_WRAPPER
366
quitcheck(xcWidget w,caddr_t clientdata,caddr_t calldata)367 int quitcheck(xcWidget w, caddr_t clientdata, caddr_t calldata)
368 {
369 char *promptstr;
370 Boolean doprompt = False;
371
372 /* enable default interrupt signal handler during this time, so that */
373 /* a double Control-C will ALWAYS exit. */
374
375 signal(SIGINT, SIG_DFL);
376
377 promptstr = (char *)malloc(60);
378 strcpy(promptstr, ".query.title.field configure -text \"Unsaved changes in: ");
379
380 /* Check all page objects for unsaved changes */
381
382 doprompt = (countchanges(&promptstr) > 0) ? True : False;
383
384 /* If any changes have not been saved, generate a prompt */
385
386 if (doprompt) {
387 promptstr = (char *)realloc(promptstr, strlen(promptstr) + 15);
388 strcat(promptstr, "\nQuit anyway?");
389
390 strcat(promptstr, "\"");
391 Tcl_Eval(xcinterp, promptstr);
392 Tcl_Eval(xcinterp, ".query.bbar.okay configure -command {quitnocheck}");
393 Tcl_Eval(xcinterp, "wm deiconify .query");
394 Tcl_Eval(xcinterp, "raise .query");
395 free(promptstr);
396 return 0;
397 }
398 else {
399 free(promptstr);
400 quit(w, NULL); // preparation for quit
401 if (calldata != (caddr_t)NULL)
402 Tcl_Eval(xcinterp, "quitnocheck intr");
403 else
404 Tcl_Eval(xcinterp, "quitnocheck"); // actual quit
405 return 1; // not reached
406 }
407 }
408
409 #endif
410
411 /*--------------------------------------*/
412 /* A gentle Ctrl-C shutdown */
413 /*--------------------------------------*/
414
dointr(int signum)415 void dointr(int signum)
416 {
417 quitcheck(NULL, NULL, (caddr_t)1);
418 }
419
420 /*--------------*/
421 /* Null routine */
422 /*--------------*/
423
DoNothing(xcWidget w,caddr_t clientdata,caddr_t calldata)424 void DoNothing(xcWidget w, caddr_t clientdata, caddr_t calldata)
425 {
426 /* Nothing here! */
427 }
428
429 /*-----------------------------------------------------------------------*/
430 /* Write page scale (overall scale, and X and Y dimensions) into strings */
431 /*-----------------------------------------------------------------------*/
432
writescalevalues(char * scdest,char * xdest,char * ydest)433 void writescalevalues(char *scdest, char *xdest, char *ydest)
434 {
435 float oscale, psscale;
436 int width, height;
437 Pagedata *curpage;
438
439 curpage = xobjs.pagelist[areawin->page];
440 oscale = curpage->outscale;
441 psscale = getpsscale(oscale, areawin->page);
442
443 width = toplevelwidth(curpage->pageinst, NULL);
444 height = toplevelheight(curpage->pageinst, NULL);
445
446 sprintf(scdest, "%6.5f", oscale);
447 if (curpage->coordstyle == CM) {
448 sprintf(xdest, "%6.5f", (width * psscale) / IN_CM_CONVERT);
449 sprintf(ydest, "%6.5f", (height * psscale) / IN_CM_CONVERT);
450 }
451 else {
452 sprintf(xdest, "%6.5f", (width * psscale) / 72.0);
453 sprintf(ydest, "%6.5f", (height * psscale) / 72.0);
454 }
455 }
456
457 /*-------------------------------------------------------------------------*/
458 /* Destroy an interactive text-editing popup box */
459 /*-------------------------------------------------------------------------*/
460
461 #ifdef TCL_WRAPPER
462
463 /* Just pop it down. . . */
464
destroypopup(xcWidget button,popupstruct * callstruct,caddr_t calldata)465 void destroypopup(xcWidget button, popupstruct *callstruct, caddr_t calldata)
466 {
467 Tk_UnmapWindow(callstruct->popup);
468 popups--;
469
470 /* free the allocated structure space */
471
472 free(callstruct->buttonptr);
473 if (callstruct->filter != NULL) free(callstruct->filter);
474 free(callstruct);
475
476 /* in the case of "quitcheck", we want to make sure that the signal */
477 /* handler is reset to default behavior if the quit command is */
478 /* canceled from inside the popup prompt window. */
479
480 signal(SIGINT, dointr);
481 }
482
483 /*-------------------------------------------------------------------------*/
484 /* Create a popup window with "OK" and "Cancel" buttons, */
485 /* and text and label fields. */
486 /*-------------------------------------------------------------------------*/
487
popupprompt(xcWidget button,char * request,char * current,void (* function)(),buttonsave * datastruct,const char * filter)488 void popupprompt(xcWidget button, char *request, char *current, void (*function)(),
489 buttonsave *datastruct, const char *filter)
490 {
491 Tk_Window popup;
492
493 popup = Tk_NameToWindow(xcinterp, ".dialog", Tk_MainWindow(xcinterp));
494 Tk_MapWindow(popup);
495 }
496
497 /*----------------------------------------------------------------------*/
498 /* Create a popup window for property changes */
499 /*----------------------------------------------------------------------*/
500
outputpopup(xcWidget button,caddr_t clientdata,caddr_t calldata)501 void outputpopup(xcWidget button, caddr_t clientdata, caddr_t calldata)
502 {
503 Tcl_Eval(xcinterp, "wm deiconify .output");
504 }
505
506 /*----------------------------------------------------------------------*/
507 /* Get the display resolution. This is needed especially for retinal */
508 /* displays with 4x the DPI of typical displays. Keeping the DPI value */
509 /* around is useful for determing a sane size for fonts, buttons, */
510 /* scrollbar widths, etc. */
511 /* */
512 /* Note that this routine does not go so far as to handle non-square */
513 /* pixels. */
514 /* */
515 /* The first check is to look for Xft.dpi in the X resource database. */
516 /* If it is not found, then fall back on the DisplayWidth as reported */
517 /* by Xlib (to do: Override both of these with an environment variable */
518 /* such as XCIRCUIT_DPI). */
519 /*----------------------------------------------------------------------*/
520
getscreenDPI()521 int getscreenDPI()
522 {
523 int x, xmm, dpi;
524
525 XrmDatabase db;
526 XrmValue val;
527 char *resource_manager;
528 char *type, *var;
529
530 XrmInitialize();
531 resource_manager = XResourceManagerString(dpy);
532
533 if (resource_manager != NULL) {
534 db = XrmGetStringDatabase(resource_manager);
535 if (db != NULL) {
536 XrmGetResource(db, "Xft.dpi", "String", &type, &val);
537 if (val.addr != NULL && !strncmp("String", type, 64))
538 if (sscanf(val.addr, "%d", &dpi) == 1)
539 return dpi;
540 }
541 }
542
543 x = DisplayWidth(dpy, DefaultScreen(dpy));
544 xmm = DisplayWidthMM(dpy, DefaultScreen(dpy));
545 dpi = (int)(((float)x + 0.5) / (float)(xmm / 25.4));
546 return dpi;
547 }
548
549 /*-------------------------------------------------*/
550 /* Print a string to the message widget. */
551 /* Note: Widget message must be a global variable. */
552 /* For formatted strings, format first into _STR */
553 /*-------------------------------------------------*/
554
555 /* XXX it looks like this routine is an orphan */
clrmessage(caddr_t clientdata)556 void clrmessage(caddr_t clientdata)
557 {
558 char buf1[50], buf2[50];
559
560 /* Don't write over the report of the edit string contents, */
561 /* if we're in one of the label edit modes */
562
563 if (eventmode == TEXT_MODE || eventmode == ETEXT_MODE)
564 charreport(TOLABEL(EDITPART));
565 else {
566 measurestr(xobjs.pagelist[areawin->page]->gridspace, buf1);
567 measurestr(xobjs.pagelist[areawin->page]->snapspace, buf2);
568 Wprintf("Grid %.50s : Snap %.50s", buf1, buf2);
569 }
570 }
571
572 #endif /* TCL_WRAPPER */
573
574 /*------------------------------------------------------------------------------*/
575 /* diagnostic tool for translating the event mode into a string and printing */
576 /* to stderr (for debugging only). */
577 /*------------------------------------------------------------------------------*/
578
printeventmode()579 void printeventmode() {
580 Fprintf(stderr, "eventmode is \'");
581 switch(eventmode) {
582 case NORMAL_MODE:
583 Fprintf(stderr, "NORMAL");
584 break;
585 case MOVE_MODE:
586 Fprintf(stderr, "MOVE");
587 break;
588 case COPY_MODE:
589 Fprintf(stderr, "COPY");
590 break;
591 case SELAREA_MODE:
592 Fprintf(stderr, "SELAREA");
593 break;
594 case CATALOG_MODE:
595 Fprintf(stderr, "CATALOG");
596 break;
597 case CATTEXT_MODE:
598 Fprintf(stderr, "CATTEXT");
599 break;
600 case CATMOVE_MODE:
601 Fprintf(stderr, "CATMOVE");
602 break;
603 case FONTCAT_MODE:
604 Fprintf(stderr, "FONTCAT");
605 break;
606 case EFONTCAT_MODE:
607 Fprintf(stderr, "EFONTCAT");
608 break;
609 case TEXT_MODE:
610 Fprintf(stderr, "TEXT");
611 break;
612 case ETEXT_MODE:
613 Fprintf(stderr, "ETEXT");
614 break;
615 case WIRE_MODE:
616 Fprintf(stderr, "WIRE");
617 break;
618 case BOX_MODE:
619 Fprintf(stderr, "BOX");
620 break;
621 case EPOLY_MODE:
622 Fprintf(stderr, "EPOLY");
623 break;
624 case ARC_MODE:
625 Fprintf(stderr, "ARC");
626 break;
627 case EARC_MODE:
628 Fprintf(stderr, "EARC");
629 break;
630 case SPLINE_MODE:
631 Fprintf(stderr, "SPLINE");
632 break;
633 case ESPLINE_MODE:
634 Fprintf(stderr, "ESPLINE");
635 break;
636 case EPATH_MODE:
637 Fprintf(stderr, "EPATH");
638 break;
639 case EINST_MODE:
640 Fprintf(stderr, "EINST");
641 break;
642 case ASSOC_MODE:
643 Fprintf(stderr, "ASSOC");
644 break;
645 case RESCALE_MODE:
646 Fprintf(stderr, "RESCALE");
647 break;
648 case PAN_MODE:
649 Fprintf(stderr, "PAN");
650 break;
651 default:
652 Fprintf(stderr, "(unknown)");
653 break;
654 }
655 Fprintf(stderr, "_MODE\'\n");
656 }
657
658 /*------------------------------------------------------------------------------*/
659
660 #ifdef TCL_WRAPPER
661
662 /*------------------------------------------------------------------------------*/
663 /* "docommand" de-iconifies the TCL console. */
664 /*------------------------------------------------------------------------------*/
665
docommand()666 void docommand()
667 {
668 Tcl_Eval(xcinterp, "catch xcircuit::raiseconsole");
669 }
670
671 /*------------------------------------------------------------------------------*/
672 /* When all else fails, install your own colormap */
673 /*------------------------------------------------------------------------------*/
674
installowncmap()675 int installowncmap()
676 {
677 Colormap newcmap;
678
679 Fprintf(stdout, "Installing my own colormap\n");
680
681 /* allocate a new colormap */
682
683 newcmap = XCopyColormapAndFree(dpy, cmap);
684 if (newcmap == (Colormap)NULL) return (-1);
685 cmap = newcmap;
686 return(1);
687 }
688
689 #endif /* TCL_WRAPPER */
690
691 /*------------------------------------------------------------------------------*/
692 /* Find the nearest color in the colormap to cvexact and return its pixel value */
693 /*------------------------------------------------------------------------------*/
694
695 #if defined(TCL_WRAPPER) || !defined(XC_WIN32)
696
findnearcolor(XColor * cvexact)697 int findnearcolor(XColor *cvexact)
698 {
699 /* find the nearest-matching color in the colormap */
700
701 int i, ncolors = DisplayCells(dpy, DefaultScreen(dpy));
702 XColor *cmcolors;
703 long rdist, bdist, gdist;
704 u_long dist, mindist;
705 int minidx;
706
707 cmcolors = (XColor *)malloc(ncolors * sizeof(XColor));
708
709 for (i = 0; i < ncolors; i++) {
710 cmcolors[i].pixel = i;
711 cmcolors[i].flags = DoRed | DoGreen | DoBlue;
712 }
713 XQueryColors(dpy, cmap, cmcolors, ncolors);
714
715 mindist = ULONG_MAX;
716 for (i = 0; i < ncolors; i++) {
717 rdist = (cmcolors[i].red - cvexact->red);
718 bdist = (cmcolors[i].blue - cvexact->blue);
719 gdist = (cmcolors[i].green - cvexact->green);
720 dist = rdist * rdist + bdist * bdist + gdist * gdist;
721 if (dist < mindist) {
722 mindist = dist;
723 minidx = i;
724 }
725 }
726 free(cmcolors);
727
728 /* if we can't get the right color or something reasonably close, */
729 /* then allocate our own colormap. If we've allocated so many */
730 /* colors that the new colormap is full, then we give up and use */
731 /* whatever color was closest, no matter how far away it was. */
732
733 /* findnearcolor already used 512 per component, so to match that, */
734 /* 3 * (512 * 512) = 786432 ~= 750000 */
735
736 if (dist > 750000) {
737 if (installowncmap() > 0) {
738 if (XAllocColor(dpy, cmap, cvexact) != 0)
739 minidx = cvexact->pixel;
740 }
741 }
742
743 return minidx;
744 }
745
746 #endif
747
748 /*----------------------------------------------------------------------*/
749 /* Return the pixel value of a database color in the colorlist vector */
750 /* Since this is generally called by defined name, e.g., */
751 /* xc_getlayoutcolor(RATSNESTCOLOR), it does not check for invalid */
752 /* values of cidx. */
753 /*----------------------------------------------------------------------*/
754
xc_getlayoutcolor(int cidx)755 int xc_getlayoutcolor(int cidx)
756 {
757 return colorlist[cidx].color.pixel;
758 }
759
760 /*----------------------------------------------------------------------*/
761 /* Obtain red, green and blue values from a color index */
762 /* the colour values range from 0 to 65535 */
763 /*----------------------------------------------------------------------*/
764
xc_get_color_rgb(unsigned long cidx,unsigned short * red,unsigned short * green,unsigned short * blue)765 void xc_get_color_rgb(unsigned long cidx, unsigned short *red,
766 unsigned short *green, unsigned short *blue)
767 {
768 XColor loccolor = {
769 .pixel = cidx,
770 .flags = DoRed | DoGreen | DoBlue
771 };
772 if (areawin->area == NULL) {
773 loccolor.red = cidx & 0xff;
774 loccolor.green = (cidx >> 8) & 0xff;
775 loccolor.blue = (cidx >> 16) & 0xff;
776 }
777 else
778 XQueryColors(dpy, cmap, &loccolor, 1);
779 *red = loccolor.red;
780 *green = loccolor.green;
781 *blue = loccolor.blue;
782 }
783
784
785 /*-------------------------------------------------------------------------*/
786 /* Hack on the resource database Color allocation */
787 /* */
788 /* This overrides the built-in routine. The difference is that if a */
789 /* color cell cannot be allocated (colormap is full), then the color */
790 /* map is searched for the closest RGB value to the named color. */
791 /* */
792 /* This code depends on Display *dpy being set: Do not install the */
793 /* converter until after XtInitialize(), when using the Xt (not Tcl) GUI. */
794 /*-------------------------------------------------------------------------*/
795
796 #if defined(TCL_WRAPPER) || !defined(XC_WIN32)
797
CvtStringToPixel(XrmValuePtr args,int * nargs,XrmValuePtr fromVal,XrmValuePtr toVal)798 caddr_t CvtStringToPixel(XrmValuePtr args, int *nargs, XrmValuePtr fromVal,
799 XrmValuePtr toVal)
800 {
801 static XColor cvcolor;
802 XColor cvexact;
803
804 if (dpy == NULL) return NULL;
805
806 if (*nargs != 0)
807 Fprintf(stderr, "String to Pixel conversion takes no arguments");
808
809 /* Color defaults to black if name is not found */
810
811 if (XAllocNamedColor(dpy, cmap, (char *)fromVal->addr, &cvcolor, &cvexact)
812 == 0) {
813 if (XLookupColor(dpy, cmap, (char *)fromVal->addr, &cvexact, &cvcolor) == 0)
814 cvcolor.pixel = BlackPixel(dpy, DefaultScreen(dpy));
815 else
816 cvcolor.pixel = findnearcolor(&cvexact);
817 }
818
819 toVal->size = sizeof(u_long);
820 toVal->addr = (caddr_t) &(cvcolor.pixel);
821 return NULL;
822 }
823
824 #endif
825
826 /*-------------------------------------------------------------------------*/
827 /* Allocate new color using the CvtStringToPixel routine */
828 /*-------------------------------------------------------------------------*/
829
830 #if defined(XC_WIN32) && !defined(TCL_WRAPPER)
831
xc_alloccolor(char * name)832 int xc_alloccolor(char *name)
833 {
834 return WinNamedColor(name);
835 }
836
837 #else
838
xc_alloccolor(char * name)839 int xc_alloccolor(char *name)
840 {
841 XrmValue fromC, toC;
842 int zval = 0, pixval;
843
844 fromC.size = strlen(name);
845 fromC.addr = name;
846
847 if (areawin && (areawin->area)) {
848 CvtStringToPixel(NULL, &zval, &fromC, &toC);
849 pixval = (int)(*((u_long *)toC.addr));
850 }
851 else {
852 int r, g, b;
853
854 /* Graphics-free batch mode. Handle basic colors plus RGB */
855 if (name[0] == '#' && (strlen(name) == 7)) {
856 sscanf(&name[1], "%2x", &r);
857 sscanf(&name[3], "%2x", &g);
858 sscanf(&name[5], "%2x", &b);
859 pixval = r + g * 256 + b * 65536;
860 }
861 else if (name[0] == '#' && (strlen(name) == 13)) {
862 sscanf(&name[1], "%4x", &r);
863 sscanf(&name[5], "%4x", &g);
864 sscanf(&name[9], "%4x", &b);
865 pixval = (r >> 8) + (g >> 8) * 256 + (b >> 8) * 65536;
866 }
867 else if (sscanf(name, "%d", &pixval) != 1) {
868 if (!strcasecmp(name, "red")) {
869 r = 255;
870 g = 0;
871 b = 0;
872 }
873 else if (!strcasecmp(name, "green")) {
874 r = 0;
875 g = 255;
876 b = 0;
877 }
878 else if (!strcasecmp(name, "blue")) {
879 r = 0;
880 g = 0;
881 b = 255;
882 }
883 else if (!strcasecmp(name, "white")) {
884 r = 255;
885 g = 255;
886 b = 255;
887 }
888 else if (!strcasecmp(name, "gray")) {
889 r = 128;
890 g = 128;
891 b = 128;
892 }
893 else if (!strcasecmp(name, "yellow")) {
894 r = 255;
895 g = 255;
896 b = 0;
897 }
898 else if (!strcasecmp(name, "gray40")) {
899 r = 102;
900 g = 102;
901 b = 102;
902 }
903 else if (!strcasecmp(name, "gray60")) {
904 r = 153;
905 g = 153;
906 b = 153;
907 }
908 else if (!strcasecmp(name, "gray80")) {
909 r = 204;
910 g = 204;
911 b = 204;
912 }
913 else if (!strcasecmp(name, "gray90")) {
914 r = 229;
915 g = 229;
916 b = 229;
917 }
918 else if (!strcasecmp(name, "green2")) {
919 r = 0;
920 g = 238;
921 b = 0;
922 }
923 else if (!strcasecmp(name, "purple")) {
924 r = 160;
925 g = 32;
926 b = 240;
927 }
928 else if (!strcasecmp(name, "steelblue2")) {
929 r = 92;
930 g = 172;
931 b = 238;
932 }
933 else if (!strcasecmp(name, "red3")) {
934 r = 205;
935 g = 0;
936 b = 0;
937 }
938 else if (!strcasecmp(name, "tan")) {
939 r = 210;
940 g = 180;
941 b = 140;
942 }
943 else if (!strcasecmp(name, "brown")) {
944 r = 165;
945 g = 42;
946 b = 42;
947 }
948 else if (!strcasecmp(name, "pink")) {
949 r = 255;
950 g = 192;
951 b = 203;
952 }
953 else {
954 // Default to black
955 r = 0;
956 g = 0;
957 b = 0;
958 }
959 pixval = r + g * 256 + b * 65536;
960 }
961 }
962
963 return pixval;
964 }
965
966 #endif
967
968 /*----------------------------------------------------------------------*/
969 /* Check if color within RGB roundoff error exists in xcircuit's color */
970 /* table. Assume 24-bit color, in which resolution can be no less than */
971 /* 256 for each color component. Visual acuity is a bit less than 24- */
972 /* bit color, so assume difference should be no less than 512 per */
973 /* component for colors to be considered "different". Psychologically, */
974 /* we should really find the just-noticable-difference for each color */
975 /* component separately! But that's too complicated for this simple */
976 /* routine. */
977 /* */
978 /* Return the table entry of the color, if it is in xcircuit's color */
979 /* table, or ERRORCOLOR if not. If it is in the color table, then */
980 /* return the actual pixel value from the table in the "pixval" pointer */
981 /*----------------------------------------------------------------------*/
982
rgb_querycolor(int red,int green,int blue,int * pixval)983 int rgb_querycolor(int red, int green, int blue, int *pixval)
984 {
985 int i;
986
987 for (i = NUMBER_OF_COLORS; i < number_colors; i++) {
988 if (abs(colorlist[i].color.red - red) < 512 &&
989 abs(colorlist[i].color.green - green) < 512 &&
990 abs(colorlist[i].color.blue - blue) < 512) {
991 if (pixval)
992 *pixval = colorlist[i].color.pixel;
993 return i;
994 break;
995 }
996 }
997 return ERRORCOLOR;
998 }
999
1000 /*-----------------------------------------------------------------------*/
1001 /* Allocate new color from RGB values (e.g., from PS file "scb" command) */
1002 /*-----------------------------------------------------------------------*/
1003
1004 #if defined(XC_WIN32) && !defined(TCL_WRAPPER)
1005
rgb_alloccolor(int red,int green,int blue)1006 int rgb_alloccolor(int red, int green, int blue)
1007 {
1008 ERROR FIXME!
1009 // XCircuit version 3.9: This no longer works. Need to
1010 // allocate the color and return an index into colorlist[].
1011
1012 return RGB(red >> 8, green >> 8, blue >> 8);
1013 }
1014
1015 #else
1016
rgb_alloccolor(int red,int green,int blue)1017 int rgb_alloccolor(int red, int green, int blue)
1018 {
1019 XColor newcolor;
1020 int pixval, tableidx; /* i, (jdk) */
1021
1022 /* if color is not already in list, try to allocate it; if allocation */
1023 /* fails, grab the closest match in the colormap. */
1024
1025 tableidx = rgb_querycolor(red, green, blue, &pixval);
1026
1027 if (tableidx < 0) {
1028 newcolor.red = red;
1029 newcolor.green = green;
1030 newcolor.blue = blue;
1031 newcolor.flags = DoRed | DoGreen | DoBlue;
1032 if (areawin->area) {
1033 if (XAllocColor(dpy, cmap, &newcolor) == 0)
1034 pixval = findnearcolor(&newcolor);
1035 else
1036 pixval = newcolor.pixel;
1037 }
1038 else {
1039 // No graphics mode: Force pixel to be 24 bit RGB
1040 pixval = (red & 0xff) | ((blue & 0xff) << 8) | ((green & 0xff) << 16);
1041 }
1042
1043 // Add this to the colormap
1044 tableidx = addnewcolorentry(pixval);
1045 }
1046 return tableidx;
1047 }
1048
1049 #endif
1050
1051 /*----------------------------------------------------------------------*/
1052 /* Query a color by name to see if it is in the color table. */
1053 /* Return the table index (NOT the pixel value) of the entry, */
1054 /* ERRORCOLOR if the color is not in the table, and BADCOLOR if the */
1055 /* color name could not be parsed by XLookupColor(). */
1056 /*----------------------------------------------------------------------*/
1057
query_named_color(char * cname)1058 int query_named_color(char *cname)
1059 {
1060 XColor cvcolor, cvexact;
1061 int tableidx, result = 0;
1062
1063 if (areawin->area)
1064 result = XLookupColor(dpy, cmap, cname, &cvexact, &cvcolor);
1065
1066 if (result == 0) return BADCOLOR;
1067
1068 tableidx = rgb_querycolor(cvcolor.red, cvcolor.green, cvcolor.blue, NULL);
1069 return tableidx;
1070 }
1071
1072 /*-------------------------------------------------------------------------*/
1073 /* Make the cursors from the cursor bit data */
1074 /*-------------------------------------------------------------------------*/
1075
makecursors()1076 void makecursors()
1077 {
1078 XColor fgcolor, bgcolor;
1079 Window win = areawin->window;
1080 #ifdef TCL_WRAPPER
1081 Tk_Uid fg_uid, bg_uid;
1082 #endif
1083
1084 if (areawin->area == NULL) return;
1085
1086 bgcolor.pixel = colorlist[BACKGROUND].color.pixel;
1087 fgcolor.pixel = colorlist[FOREGROUND].color.pixel;
1088 XQueryColors(dpy, cmap, &fgcolor, 1);
1089 XQueryColors(dpy, cmap, &bgcolor, 1);
1090
1091 #ifdef TCL_WRAPPER
1092
1093 #ifdef XC_WIN32
1094 #define Tk_GetCursorFromData CreateW32Cursor
1095 #endif
1096
1097 fg_uid = Tk_GetUid(Tk_NameOfColor(&fgcolor));
1098 bg_uid = Tk_GetUid(Tk_NameOfColor(&bgcolor));
1099
1100 ARROW = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1101 arrow_bits, arrowmask_bits, arrow_width, arrow_height, arrow_x_hot, arrow_y_hot,
1102 fg_uid, bg_uid);
1103 CROSS = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1104 cross_bits, crossmask_bits, cross_width, cross_height, cross_x_hot, cross_y_hot,
1105 fg_uid, bg_uid);
1106 SCISSORS = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1107 scissors_bits, scissorsmask_bits, scissors_width, scissors_height,
1108 scissors_x_hot, scissors_y_hot, fg_uid, bg_uid);
1109 EDCURSOR = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1110 exx_bits, exxmask_bits, exx_width, exx_height, exx_x_hot, exx_y_hot,
1111 fg_uid, bg_uid);
1112 COPYCURSOR = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1113 copy_bits, copymask_bits, copy_width, copy_height, copy_x_hot, copy_y_hot,
1114 fg_uid, bg_uid);
1115 ROTATECURSOR = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1116 rot_bits, rotmask_bits, rot_width, rot_height, circle_x_hot, circle_y_hot,
1117 fg_uid, bg_uid);
1118 QUESTION = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1119 question_bits, questionmask_bits, question_width, question_height,
1120 question_x_hot, question_y_hot, fg_uid, bg_uid);
1121 CIRCLE = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1122 circle_bits, circlemask_bits, circle_width, circle_height, circle_x_hot,
1123 circle_y_hot, fg_uid, bg_uid);
1124 HAND = (Cursor)Tk_GetCursorFromData(xcinterp, Tk_IdToWindow(dpy, win),
1125 hand_bits, handmask_bits, hand_width, hand_height, hand_x_hot, hand_y_hot,
1126 fg_uid, bg_uid);
1127
1128 #ifdef XC_WIN32
1129 #undef Tk_GetCursorFromData
1130 #endif
1131
1132 #else
1133 ARROW = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, arrow_bits,
1134 arrow_width, arrow_height), XCreateBitmapFromData(dpy, win, arrowmask_bits,
1135 arrow_width, arrow_height), &fgcolor, &bgcolor, arrow_x_hot, arrow_y_hot);
1136 CROSS = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, cross_bits,
1137 cross_width, cross_height), XCreateBitmapFromData(dpy, win, crossmask_bits,
1138 cross_width, cross_height), &fgcolor, &bgcolor, cross_x_hot, cross_y_hot);
1139 SCISSORS = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, scissors_bits,
1140 scissors_width, scissors_height), XCreateBitmapFromData(dpy, win,
1141 scissorsmask_bits, scissors_width, scissors_height), &fgcolor,
1142 &bgcolor, scissors_x_hot, scissors_y_hot);
1143 EDCURSOR = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, exx_bits,
1144 exx_width, exx_height), XCreateBitmapFromData(dpy, win, exxmask_bits,
1145 exx_width, exx_height), &fgcolor, &bgcolor, exx_x_hot, exx_y_hot);
1146 COPYCURSOR = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, copy_bits,
1147 copy_width, copy_height), XCreateBitmapFromData(dpy, win, copymask_bits,
1148 copy_width, copy_height), &fgcolor, &bgcolor, copy_x_hot, copy_y_hot);
1149 ROTATECURSOR = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, rot_bits,
1150 rot_width, rot_height), XCreateBitmapFromData(dpy, win, rotmask_bits,
1151 rot_width, rot_height), &fgcolor, &bgcolor, circle_x_hot, circle_y_hot);
1152 QUESTION = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, question_bits,
1153 question_width, question_height), XCreateBitmapFromData(dpy, win,
1154 questionmask_bits, question_width, question_height),
1155 &fgcolor, &bgcolor, question_x_hot, question_y_hot);
1156 CIRCLE = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, circle_bits,
1157 circle_width, circle_height), XCreateBitmapFromData(dpy, win, circlemask_bits,
1158 circle_width, circle_height), &fgcolor, &bgcolor, circle_x_hot, circle_y_hot);
1159 HAND = XCreatePixmapCursor(dpy, XCreateBitmapFromData(dpy, win, hand_bits,
1160 hand_width, hand_height), XCreateBitmapFromData(dpy, win, handmask_bits,
1161 hand_width, hand_height), &fgcolor, &bgcolor, hand_x_hot, hand_y_hot);
1162 #endif
1163
1164 TEXTPTR = XCreateFontCursor(dpy, XC_xterm);
1165 WAITFOR = XCreateFontCursor(dpy, XC_watch);
1166
1167 XRecolorCursor(dpy, TEXTPTR, &fgcolor, &bgcolor);
1168 }
1169
1170 /*----------------------------------------------------------------------*/
1171 /* Remove a window structure and deallocate all memory used by it. */
1172 /* If it is the last window, this is equivalent to calling "quit". */
1173 /*----------------------------------------------------------------------*/
1174
delete_window(XCWindowData * window)1175 void delete_window(XCWindowData *window)
1176 {
1177 XCWindowData *searchwin, *lastwin = NULL;
1178
1179 if (xobjs.windowlist->next == NULL) {
1180 quitcheck((window == NULL) ? NULL : window->area, NULL, NULL);
1181 return;
1182 }
1183
1184 for (searchwin = xobjs.windowlist; searchwin != NULL; searchwin =
1185 searchwin->next) {
1186 if (searchwin == window) {
1187 Matrixptr thismat;
1188
1189 /* Free any select list */
1190 if (searchwin->selects > 0) free(searchwin->selectlist);
1191
1192 /* Free the matrix and pushlist stacks */
1193
1194 while (searchwin->MatStack != NULL) {
1195 thismat = searchwin->MatStack;
1196 searchwin->MatStack = searchwin->MatStack->nextmatrix;
1197 free(thismat);
1198 }
1199 free_stack(&searchwin->hierstack);
1200 free_stack(&searchwin->stack);
1201
1202 /* Free the GC */
1203 XFreeGC(dpy, searchwin->gc);
1204
1205 if (lastwin != NULL)
1206 lastwin->next = searchwin->next;
1207 else
1208 xobjs.windowlist = searchwin->next;
1209 break;
1210 }
1211 lastwin = searchwin;
1212 }
1213
1214 if (searchwin == NULL) {
1215 Wprintf("No such window in list!\n");
1216 }
1217 else {
1218 if (areawin == searchwin) areawin = xobjs.windowlist;
1219 free(searchwin);
1220 }
1221 }
1222
1223 /*----------------------------------------------------------------------*/
1224 /* Create a new window structure and initialize it. */
1225 /* Return a pointer to the new window. */
1226 /*----------------------------------------------------------------------*/
1227
create_new_window()1228 XCWindowData *create_new_window()
1229 {
1230 XCWindowData *newwindow;
1231
1232 newwindow = (XCWindowData *)malloc(sizeof(XCWindowData));
1233
1234 #ifndef TCL_WRAPPER
1235 #ifdef HAVE_XPM
1236 newwindow->toolbar_on = True;
1237 #endif
1238 #endif
1239
1240 newwindow->area = (xcWidget)NULL;
1241 newwindow->mapped = False;
1242 newwindow->psfont = 0;
1243 newwindow->anchor = FLIPINV;
1244 newwindow->page = 0;
1245 newwindow->MatStack = NULL;
1246 newwindow->textscale = 1.0;
1247 newwindow->linewidth = 1.0;
1248 newwindow->zoomfactor = SCALEFAC;
1249 newwindow->style = UNCLOSED;
1250 newwindow->invert = False;
1251 newwindow->axeson = True;
1252 newwindow->snapto = True;
1253 newwindow->gridon = True;
1254 newwindow->center = True;
1255 newwindow->bboxon = False;
1256 newwindow->filter = ALL_TYPES;
1257 newwindow->editinplace = True;
1258 newwindow->selects = 0;
1259 newwindow->selectlist = NULL;
1260 newwindow->lastlibrary = 0;
1261 newwindow->manhatn = False;
1262 newwindow->boxedit = MANHATTAN;
1263 newwindow->pathedit = TANGENTS;
1264 newwindow->lastbackground = NULL;
1265 newwindow->editstack = (objectptr) malloc(sizeof(object));
1266 newwindow->stack = NULL; /* at the top of the hierarchy */
1267 newwindow->hierstack = NULL;
1268 initmem(newwindow->editstack);
1269 newwindow->pinpointon = False;
1270 newwindow->showclipmasks = True;
1271 newwindow->pinattach = False;
1272 newwindow->buschar = '('; /* Vector notation for buses */
1273 newwindow->defaultcursor = &CROSS;
1274 newwindow->event_mode = NORMAL_MODE;
1275 newwindow->attachto = -1;
1276 newwindow->color = DEFAULTCOLOR;
1277 newwindow->gccolor = 0;
1278 newwindow->time_id = 0;
1279 newwindow->redraw_needed = True;
1280 newwindow->redraw_ongoing = False;
1281 #ifdef HAVE_CAIRO
1282 newwindow->fixed_pixmap = NULL;
1283 newwindow->cr = NULL;
1284 #else /* HAVE_CAIRO */
1285 newwindow->clipmask = (Pixmap)NULL;
1286 newwindow->pbuf = (Pixmap)NULL;
1287 newwindow->cmgc = (GC)NULL;
1288 newwindow->clipped = 0;
1289 newwindow->fixed_pixmap = (Pixmap) NULL;
1290 #endif /* !HAVE_CAIRO */
1291 newwindow->vscale = 1;
1292 newwindow->pcorner.x = newwindow->pcorner.y = 0;
1293 newwindow->topinstance = (objinstptr)NULL;
1294 newwindow->panx = newwindow->pany = 0;
1295
1296 /* Prepend to linked window list in global data (xobjs) */
1297 newwindow->next = xobjs.windowlist;
1298 xobjs.windowlist = newwindow;
1299
1300 return newwindow;
1301 }
1302
1303 /*----------------------------------------------------------------------*/
1304 /* Preparatory initialization (to be run before setting up the GUI) */
1305 /*----------------------------------------------------------------------*/
1306
pre_initialize()1307 void pre_initialize()
1308 {
1309 short i, page;
1310
1311 /*-------------------------------------------------------------*/
1312 /* Force LC_NUMERIC locale to en_US for decimal point = period */
1313 /* notation. The environment variable LC_NUMERIC overrides if */
1314 /* it is set explicitly, so it has to be unset first to allow */
1315 /* setlocale() to work. */
1316 /*-------------------------------------------------------------*/
1317
1318 #ifdef HAVE_PUTENV
1319 putenv("LC_ALL=en_US");
1320 putenv("LC_NUMERIC=en_US");
1321 putenv("LANG=POSIX");
1322 #else
1323 unsetenv("LC_ALL");
1324 unsetenv("LC_NUMERIC");
1325 setenv("LANG", "POSIX", 1);
1326 #endif
1327 setlocale(LC_ALL, "en_US");
1328
1329 /*---------------------------*/
1330 /* initialize user variables */
1331 /*---------------------------*/
1332
1333 strcpy(version, PROG_VERSION);
1334 aliastop = NULL;
1335 xobjs.pagelist = (Pagedata **) malloc(PAGES * sizeof(Pagedata *));
1336 for (page = 0; page < PAGES; page++) {
1337 xobjs.pagelist[page] = (Pagedata *) malloc(sizeof(Pagedata));
1338 xobjs.pagelist[page]->pageinst = NULL;
1339 xobjs.pagelist[page]->filename = NULL;
1340 }
1341 /* Set values for the first page */
1342 xobjs.pagelist[0]->wirewidth = 2.0;
1343 xobjs.pagelist[0]->outscale = 1.0;
1344 xobjs.pagelist[0]->background.name = (char *)NULL;
1345 xobjs.pagelist[0]->pmode = 2; /* set auto-fit ON by default */
1346 xobjs.pagelist[0]->orient = 0;
1347 xobjs.pagelist[0]->gridspace = DEFAULTGRIDSPACE;
1348 xobjs.pagelist[0]->snapspace = DEFAULTSNAPSPACE;
1349 xobjs.pagelist[0]->drawingscale.x = xobjs.pagelist[0]->drawingscale.y = 1;
1350 xobjs.pagelist[0]->coordstyle = INTERNAL;
1351 xobjs.pagelist[0]->pagesize.x = 612;
1352 xobjs.pagelist[0]->pagesize.y = 792;
1353 xobjs.pagelist[0]->margins.x = 72;
1354 xobjs.pagelist[0]->margins.y = 72;
1355
1356 xobjs.hold = TRUE;
1357 xobjs.showtech = FALSE;
1358 xobjs.suspend = (signed char)0; /* Suspend graphics until finished with startup */
1359 xobjs.new_changes = 0;
1360 xobjs.filefilter = TRUE;
1361 xobjs.tempfile = NULL;
1362 xobjs.retain_backup = False; /* default: remove backup after file write */
1363 signal(SIGINT, dointr);
1364 printtime_id = 0;
1365
1366 xobjs.technologies = NULL;
1367 xobjs.undostack = NULL;
1368 xobjs.redostack = NULL;
1369
1370 /* Set the temporary directory name as compiled, unless overridden by */
1371 /* environment variable "TMPDIR". */
1372
1373 xobjs.tempdir = getenv("TMPDIR");
1374 if (xobjs.tempdir == NULL) xobjs.tempdir = strdup(TEMP_DIR);
1375
1376 xobjs.windowlist = (XCWindowDataPtr)NULL;
1377 areawin = NULL;
1378
1379 xobjs.numlibs = LIBS - LIBRARY - 1;
1380 xobjs.fontlib.number = 0;
1381 xobjs.userlibs = (Library *) malloc(xobjs.numlibs * sizeof(Library));
1382 for (i = 0; i < xobjs.numlibs; i++) {
1383 xobjs.userlibs[i].library = (objectptr *) malloc(sizeof(objectptr));
1384 xobjs.userlibs[i].instlist = NULL;
1385 xobjs.userlibs[i].number = 0;
1386 }
1387 xobjs.imagelist = NULL;
1388 xobjs.images = 0;
1389 xobjs.pages = PAGES;
1390
1391 xobjs.libsearchpath = (char *)NULL;
1392 xobjs.filesearchpath = (char *)NULL;
1393
1394 fontcount = 0;
1395 fonts = (fontinfo *) malloc(sizeof(fontinfo));
1396 fonts[0].encoding = NULL; /* To prevent segfaults */
1397 fonts[0].psname = NULL;
1398 fonts[0].family = NULL;
1399
1400 /* Initialization of objects requires values for the window width and height, */
1401 /* so set up the widgets and realize them first. */
1402
1403 popups = 0; /* no popup windows yet */
1404 beeper = 1; /* Ring bell on certain warnings or errors */
1405 pressmode = 0; /* not in a button press & hold mode yet */
1406 initsplines(); /* create lookup table of spline parameters */
1407 }
1408
1409 #ifdef TCL_WRAPPER
1410
1411 /*----------------------------------------------------------------------*/
1412 /* Create a new Handle object in Tcl */
1413 /*----------------------------------------------------------------------*/
1414
1415 static void UpdateStringOfHandle _ANSI_ARGS_((Tcl_Obj *objPtr));
1416 static int SetHandleFromAny _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr));
1417
1418 static Tcl_ObjType tclHandleType = {
1419 "handle", /* name */
1420 (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */
1421 (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */
1422 UpdateStringOfHandle, /* updateStringProc */
1423 SetHandleFromAny /* setFromAnyProc */
1424 };
1425
1426 /*----------------------------------------------------------------------*/
1427
1428 static void
UpdateStringOfHandle(objPtr)1429 UpdateStringOfHandle(objPtr)
1430 Tcl_Obj *objPtr; /* Int object whose string rep to update. */
1431 {
1432 char buffer[TCL_INTEGER_SPACE];
1433 int len;
1434
1435 sprintf(buffer, "H%08lX", objPtr->internalRep.longValue);
1436 len = strlen(buffer);
1437
1438 objPtr->bytes = Tcl_Alloc((u_int)len + 1);
1439 strcpy(objPtr->bytes, buffer);
1440 objPtr->length = len;
1441 }
1442
1443 /*----------------------------------------------------------------------*/
1444
1445 static int
SetHandleFromAny(interp,objPtr)1446 SetHandleFromAny(interp, objPtr)
1447 Tcl_Interp *interp; /* Used for error reporting if not NULL. */
1448 Tcl_Obj *objPtr; /* The object to convert. */
1449 {
1450 Tcl_ObjType *oldTypePtr = (Tcl_ObjType *)objPtr->typePtr;
1451 char *string, *end;
1452 int length;
1453 char *p;
1454 long newLong;
1455 pushlistptr newstack = NULL;
1456
1457 string = Tcl_GetStringFromObj(objPtr, &length);
1458 errno = 0;
1459 for (p = string; isspace((u_char)(*p)); p++);
1460
1461 nexthier:
1462
1463 if (*p++ != 'H') {
1464 if (interp != NULL) {
1465 Tcl_ResetResult(interp);
1466 Tcl_AppendToObj(Tcl_GetObjResult(interp),
1467 "handle is identified by leading H and hexidecimal value only", -1);
1468 }
1469 free_stack(&newstack);
1470 return TCL_ERROR;
1471 } else {
1472 newLong = strtoul(p, &end, 16);
1473 }
1474 if (end == p) {
1475 badHandle:
1476 if (interp != NULL) {
1477 /*
1478 * Must copy string before resetting the result in case a caller
1479 * is trying to convert the interpreter's result to an int.
1480 */
1481
1482 char buf[100];
1483 sprintf(buf, "expected handle but got \"%.50s\"", string);
1484 Tcl_ResetResult(interp);
1485 Tcl_AppendToObj(Tcl_GetObjResult(interp), buf, -1);
1486 }
1487 free_stack(&newstack);
1488 return TCL_ERROR;
1489 }
1490 if (errno == ERANGE) {
1491 if (interp != NULL) {
1492 char *s = "handle value too large to represent";
1493 Tcl_ResetResult(interp);
1494 Tcl_AppendToObj(Tcl_GetObjResult(interp), s, -1);
1495 Tcl_SetErrorCode(interp, "ARITH", "IOVERFLOW", s, (char *) NULL);
1496 }
1497 free_stack(&newstack);
1498 return TCL_ERROR;
1499 }
1500 /*
1501 * Make sure that the string has no garbage after the end of the handle.
1502 */
1503
1504 while ((end < (string+length)) && isspace((u_char)(*end))) end++;
1505 if (end != (string+length)) {
1506 /* Check for handles separated by slashes. If present, */
1507 /* then generate a hierstack. */
1508
1509 if ((end != NULL) && (*end == '/')) {
1510 objinstptr refinst, chkinst;
1511 genericptr *rgen;
1512
1513 *end = '\0';
1514 newLong = strtoul(p, &end, 16);
1515 p = end + 1;
1516 *end = '/';
1517 refinst = (newstack == NULL) ? areawin->topinstance : newstack->thisinst;
1518 chkinst = (objinstptr)((pointertype)(newLong));
1519 /* Ensure that chkinst is in the plist of */
1520 /* refinst->thisobject, and that it is type objinst. */
1521 for (rgen = refinst->thisobject->plist; rgen < refinst->thisobject->plist
1522 + refinst->thisobject->parts; rgen++) {
1523 if ((objinstptr)(*rgen) == chkinst) {
1524 if (ELEMENTTYPE(*rgen) != OBJINST) {
1525 free_stack(&newstack);
1526 Tcl_SetResult(interp, "Hierarchical element handle "
1527 "component is not an object instance.", NULL);
1528 return TCL_ERROR;
1529 }
1530 break;
1531 }
1532 }
1533 if (rgen == refinst->thisobject->plist + refinst->thisobject->parts) {
1534 Tcl_SetResult(interp, "Bad component in hierarchical "
1535 "element handle.", NULL);
1536 free_stack(&newstack);
1537 return TCL_ERROR;
1538 }
1539 push_stack(&newstack, chkinst, NULL);
1540 goto nexthier;
1541 }
1542 else
1543 goto badHandle;
1544 }
1545
1546 /* Note that this check won't prevent a hierarchical selection from */
1547 /* being added to a non-hierarchical selection. */
1548
1549 if (areawin->hierstack != NULL) {
1550 if ((newstack == NULL) || (newstack->thisinst !=
1551 areawin->hierstack->thisinst)) {
1552 Tcl_SetResult(interp, "Attempt to select components in different "
1553 "objects.", NULL);
1554 free_stack(&newstack);
1555 return TCL_ERROR;
1556 }
1557 }
1558 free_stack(&areawin->hierstack);
1559 areawin->hierstack = newstack;
1560
1561 /*
1562 * The conversion to handle succeeded. Free the old internalRep before
1563 * setting the new one. We do this as late as possible to allow the
1564 * conversion code, in particular Tcl_GetStringFromObj, to use that old
1565 * internalRep.
1566 */
1567
1568 if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
1569 oldTypePtr->freeIntRepProc(objPtr);
1570 }
1571
1572 objPtr->internalRep.longValue = newLong;
1573 objPtr->typePtr = &tclHandleType;
1574 return TCL_OK;
1575 }
1576
1577 /*----------------------------------------------------------------------*/
1578
1579 Tcl_Obj *
Tcl_NewHandleObj(optr)1580 Tcl_NewHandleObj(optr)
1581 void *optr; /* Int used to initialize the new object. */
1582 {
1583 Tcl_Obj *objPtr;
1584
1585 objPtr = Tcl_NewObj();
1586 objPtr->bytes = NULL;
1587
1588 objPtr->internalRep.longValue = (long)(optr);
1589 objPtr->typePtr = &tclHandleType;
1590 return objPtr;
1591 }
1592
1593 /*----------------------------------------------------------------------*/
1594
1595 int
Tcl_GetHandleFromObj(interp,objPtr,handlePtr)1596 Tcl_GetHandleFromObj(interp, objPtr, handlePtr)
1597 Tcl_Interp *interp; /* Used for error reporting if not NULL. */
1598 Tcl_Obj *objPtr; /* The object from which to get a int. */
1599 void **handlePtr; /* Place to store resulting int. */
1600 {
1601 long l;
1602 int result;
1603
1604 if (objPtr->typePtr != &tclHandleType) {
1605 result = SetHandleFromAny(interp, objPtr);
1606 if (result != TCL_OK) {
1607 return result;
1608 }
1609 }
1610 l = objPtr->internalRep.longValue;
1611
1612 #if SIZEOF_VOID_P == SIZEOF_UNSIGNED_INT
1613 if (((long)((int)l)) == l) {
1614 *handlePtr = (void *)objPtr->internalRep.longValue;
1615 return TCL_OK;
1616 }
1617 #else
1618 #if SIZEOF_VOID_P == SIZEOF_UNSIGNED_LONG
1619 *handlePtr = (void *)objPtr->internalRep.longValue;
1620 return TCL_OK;
1621 #endif
1622 #endif
1623
1624 if (interp != NULL) {
1625 Tcl_ResetResult(interp);
1626 Tcl_AppendToObj(Tcl_GetObjResult(interp),
1627 "value too large to represent as handle", -1);
1628 }
1629 return TCL_ERROR;
1630 }
1631
1632
1633 #endif
1634
1635 /*----------------------------------------------------------------------*/
1636 /* Routine to initialize variables after the GUI has been set up */
1637 /*----------------------------------------------------------------------*/
1638
post_initialize()1639 void post_initialize()
1640 {
1641 short i;
1642
1643 /*--------------------------------------------------*/
1644 /* Setup the (simple) colormap and make the cursors */
1645 /*--------------------------------------------------*/
1646
1647 setcolorscheme(True);
1648 makecursors();
1649
1650 /* Now that we have values for the window width and height, we can initialize */
1651 /* the page objects. */
1652
1653 xobjs.libtop = (objinstptr *)malloc(LIBS * sizeof(objinstptr));
1654 for (i = 0; i < LIBS; i++) {
1655 objectptr newlibobj = (objectptr) malloc(sizeof(object));
1656 initmem(newlibobj);
1657 xobjs.libtop[i] = newpageinst(newlibobj);
1658 }
1659
1660 /* Give names to the five default libraries */
1661 strcpy(xobjs.libtop[FONTLIB]->thisobject->name, "Font Character List");
1662 strcpy(xobjs.libtop[PAGELIB]->thisobject->name, "Page Directory");
1663 strcpy(xobjs.libtop[LIBLIB]->thisobject->name, "Library Directory");
1664 strcpy(xobjs.libtop[USERLIB]->thisobject->name, "User Library");
1665 renamelib(USERLIB);
1666
1667 changepage(0);
1668
1669 /* Centering the view is not required here because the default values */
1670 /* set in initmem() should correctly position the empty page in the */
1671 /* middle of the viewing window. */
1672
1673 #if !defined(HAVE_CAIRO)
1674 if ((dbuf == (Pixmap)NULL) && areawin->area)
1675 dbuf = XCreatePixmap(dpy, areawin->window, areawin->width,
1676 areawin->height, DefaultDepthOfScreen(xcScreen(areawin->area)));
1677 #endif
1678
1679 /* Set up fundamentally necessary colors black and white */
1680
1681 addnewcolorentry(xc_alloccolor("Black"));
1682 addnewcolorentry(xc_alloccolor("White"));
1683
1684 #ifdef TCL_WRAPPER
1685
1686 /* Set up new Tcl type "handle" for element handles */
1687 Tcl_RegisterObjType(&tclHandleType);
1688
1689 #endif
1690
1691 /*-----------------------------------------------------*/
1692 /* Set the cursor as a crosshair for the area widget. */
1693 /* If areawin->area is NULL, then xcircuit is running */
1694 /* in batch mode, and no automatic save will be done. */
1695 /*-----------------------------------------------------*/
1696
1697 if (areawin->area != NULL) {
1698 XDefineCursor (dpy, areawin->window, DEFAULTCURSOR);
1699
1700 /*---------------------------------------------------*/
1701 /* Set up a timeout for automatic save to a tempfile */
1702 /*---------------------------------------------------*/
1703
1704 xobjs.save_interval = appdata.timeout;
1705 xobjs.timeout_id = xcAddTimeOut(app, 60000 * xobjs.save_interval,
1706 savetemp, NULL);
1707 }
1708 }
1709
1710 /*----------------------------------------------------------------------*/
1711