Made a change to the font loading with XLoadQueryFont() to fall
[xcircuit.git] / xcircuit.c
blob9d7e26782893ac9edafdbef02c76f30b1af40e63
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 /*----------------------------------------------------------------------*/
24 /*----------------------------------------------------------------------*/
25 /* Uses the Xw widget set (see directory Xw) */
26 /* Xcircuit written by Tim Edwards beginning 8/13/93 */
27 /*----------------------------------------------------------------------*/
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() */
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
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
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
78 #ifndef P_tmpdir
79 #define P_tmpdir TMPDIR
80 #endif
82 /*----------------------------------------------------------------------*/
83 /* Local includes */
84 /*----------------------------------------------------------------------*/
86 #include "xcircuit.h"
87 #include "cursors.h"
88 #include "colordefs.h"
89 #include "menudep.h"
91 /*----------------------------------------------------------------------*/
92 /* Function prototype declarations */
93 /*----------------------------------------------------------------------*/
94 #include "prototypes.h"
96 /*----------------------------------------------------------------------*/
97 /* Global Variable definitions */
98 /*----------------------------------------------------------------------*/
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 */
120 /*----------------------------------------------------------------------*/
121 /* Externally defined variables */
122 /*----------------------------------------------------------------------*/
124 extern aliasptr aliastop;
125 extern char version[];
127 #ifdef TCL_WRAPPER
128 extern Tcl_Interp *xcinterp;
129 #else
130 extern XtAppContext app;
131 #endif
133 #if !defined(HAVE_CAIRO)
134 extern Pixmap dbuf;
135 #endif
137 /* Bad hack for problems with the DECstation. . . don't know why */
138 #ifdef UniqueContextProblem
139 #undef XUniqueContext
140 XContext XUniqueContext()
142 return XrmUniqueQuark();
144 #endif
145 /* End of bad hack. . . */
147 /*----------------------------------------------------------------------*/
148 /* Update the list of colors in the colormap */
149 /*----------------------------------------------------------------------*/
151 void addtocolorlist(xcWidget button, int cvalue)
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;
159 /* Get and store the RGB values of the new color */
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);
166 else
167 XQueryColors(dpy, cmap, &(colorlist[number_colors - 1].color), 1);
170 /*----------------------------------------------------------------------*/
171 /* Add a new color button to the color menu */
172 /* called if new color button needs to be made */
173 /*----------------------------------------------------------------------*/
175 #ifdef TCL_WRAPPER
177 int addnewcolorentry(int ccolor)
179 xcWidget newbutton = (xcWidget)NULL;
180 int i;
182 /* check to see if entry is already in the color list */
184 for (i = NUMBER_OF_COLORS; i < number_colors; i++)
185 if (colorlist[i].color.pixel == ccolor) break;
187 /* make new entry in the menu */
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);
197 return i;
200 #endif /* TCL_WRAPPER */
202 /*---------------*/
203 /* Quit xcircuit */
204 /*---------------*/
206 void quit(xcWidget w, caddr_t nulldata)
208 int i;
209 Matrixptr curmatrix, dmatrix;
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;
219 areawin->MatStack = NULL;
222 /* free the colormap if a new one has been installed */
224 if (dpy != NULL)
225 if (cmap != DefaultColormap(dpy, DefaultScreen(dpy)))
226 XFreeColormap(dpy, cmap);
228 /* exit ghostscript if background rendering was enabled */
230 exit_gs();
232 #ifdef TCL_WRAPPER
233 /* exit ngspice if the simulator is still running */
235 exit_spice();
236 #endif
238 /* remove any temporary files created for background rendering */
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);
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. */
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
263 else
264 Fprintf(stderr, "Ctrl-C exit: reload workspace from \"%s\"\n",
265 xobjs.tempfile);
266 free(xobjs.tempfile);
267 xobjs.tempfile = NULL;
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
284 /*--------------------------------------------------------------*/
285 /* Check for changes in an object and all of its descendents */
286 /*--------------------------------------------------------------*/
288 u_short getchanges(objectptr thisobj)
290 genericptr *pgen;
291 objinstptr pinst;
292 u_short changes = thisobj->changes;
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);
300 return changes;
303 /*--------------------------------------------------------------*/
304 /* Check to see if any objects in xcircuit have been modified */
305 /* without saving. */
306 /*--------------------------------------------------------------*/
308 u_short countchanges(char **promptstr)
310 int slen = 1, i; /* , j; (jdk) */
311 u_short locchanges, changes = 0, words = 1;
312 objectptr thisobj;
313 char *fname;
314 TechPtr ns;
316 if (promptstr != NULL) slen += strlen(*promptstr);
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++;
331 changes += locchanges;
336 /* Check all library objects for unsaved changes */
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++;
355 return changes;
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 /*----------------------------------------------*/
365 #ifdef TCL_WRAPPER
367 int quitcheck(xcWidget w, caddr_t clientdata, caddr_t calldata)
369 char *promptstr;
370 Boolean doprompt = False;
372 /* enable default interrupt signal handler during this time, so that */
373 /* a double Control-C will ALWAYS exit. */
375 signal(SIGINT, SIG_DFL);
377 promptstr = (char *)malloc(60);
378 strcpy(promptstr, ".query.title.field configure -text \"Unsaved changes in: ");
380 /* Check all page objects for unsaved changes */
382 doprompt = (countchanges(&promptstr) > 0) ? True : False;
384 /* If any changes have not been saved, generate a prompt */
386 if (doprompt) {
387 promptstr = (char *)realloc(promptstr, strlen(promptstr) + 15);
388 strcat(promptstr, "\nQuit anyway?");
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;
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
409 #endif
411 /*--------------------------------------*/
412 /* A gentle Ctrl-C shutdown */
413 /*--------------------------------------*/
415 void dointr(int signum)
417 quitcheck(NULL, NULL, (caddr_t)1);
420 /*--------------*/
421 /* Null routine */
422 /*--------------*/
424 void DoNothing(xcWidget w, caddr_t clientdata, caddr_t calldata)
426 /* Nothing here! */
429 /*-----------------------------------------------------------------------*/
430 /* Write page scale (overall scale, and X and Y dimensions) into strings */
431 /*-----------------------------------------------------------------------*/
433 void writescalevalues(char *scdest, char *xdest, char *ydest)
435 float oscale, psscale;
436 int width, height;
437 Pagedata *curpage;
439 curpage = xobjs.pagelist[areawin->page];
440 oscale = curpage->outscale;
441 psscale = getpsscale(oscale, areawin->page);
443 width = toplevelwidth(curpage->pageinst, NULL);
444 height = toplevelheight(curpage->pageinst, NULL);
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);
451 else {
452 sprintf(xdest, "%6.5f", (width * psscale) / 72.0);
453 sprintf(ydest, "%6.5f", (height * psscale) / 72.0);
457 /*-------------------------------------------------------------------------*/
458 /* Destroy an interactive text-editing popup box */
459 /*-------------------------------------------------------------------------*/
461 #ifdef TCL_WRAPPER
463 /* Just pop it down. . . */
465 void destroypopup(xcWidget button, popupstruct *callstruct, caddr_t calldata)
467 Tk_UnmapWindow(callstruct->popup);
468 popups--;
470 /* free the allocated structure space */
472 free(callstruct->buttonptr);
473 if (callstruct->filter != NULL) free(callstruct->filter);
474 free(callstruct);
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. */
480 signal(SIGINT, dointr);
483 /*-------------------------------------------------------------------------*/
484 /* Create a popup window with "OK" and "Cancel" buttons, */
485 /* and text and label fields. */
486 /*-------------------------------------------------------------------------*/
488 void popupprompt(xcWidget button, char *request, char *current, void (*function)(),
489 buttonsave *datastruct, const char *filter)
491 Tk_Window popup;
493 popup = Tk_NameToWindow(xcinterp, ".dialog", Tk_MainWindow(xcinterp));
494 Tk_MapWindow(popup);
497 /*----------------------------------------------------------------------*/
498 /* Create a popup window for property changes */
499 /*----------------------------------------------------------------------*/
501 void outputpopup(xcWidget button, caddr_t clientdata, caddr_t calldata)
503 Tcl_Eval(xcinterp, "wm deiconify .output");
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 /*----------------------------------------------------------------------*/
521 int getscreenDPI()
523 int x, xmm, dpi;
525 XrmDatabase db;
526 XrmValue val;
527 char *resource_manager;
528 char *type, *var;
530 XrmInitialize();
531 resource_manager = XResourceManagerString(dpy);
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;
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;
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 /*-------------------------------------------------*/
555 /* XXX it looks like this routine is an orphan */
556 void clrmessage(caddr_t clientdata)
558 char buf1[50], buf2[50];
560 /* Don't write over the report of the edit string contents, */
561 /* if we're in one of the label edit modes */
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);
572 #endif /* TCL_WRAPPER */
574 /*------------------------------------------------------------------------------*/
575 /* diagnostic tool for translating the event mode into a string and printing */
576 /* to stderr (for debugging only). */
577 /*------------------------------------------------------------------------------*/
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;
655 Fprintf(stderr, "_MODE\'\n");
658 /*------------------------------------------------------------------------------*/
660 #ifdef TCL_WRAPPER
662 /*------------------------------------------------------------------------------*/
663 /* "docommand" de-iconifies the TCL console. */
664 /*------------------------------------------------------------------------------*/
666 void docommand()
668 Tcl_Eval(xcinterp, "catch xcircuit::raiseconsole");
671 /*------------------------------------------------------------------------------*/
672 /* When all else fails, install your own colormap */
673 /*------------------------------------------------------------------------------*/
675 int installowncmap()
677 Colormap newcmap;
679 Fprintf(stdout, "Installing my own colormap\n");
681 /* allocate a new colormap */
683 newcmap = XCopyColormapAndFree(dpy, cmap);
684 if (newcmap == (Colormap)NULL) return (-1);
685 cmap = newcmap;
686 return(1);
689 #endif /* TCL_WRAPPER */
691 /*------------------------------------------------------------------------------*/
692 /* Find the nearest color in the colormap to cvexact and return its pixel value */
693 /*------------------------------------------------------------------------------*/
695 #if defined(TCL_WRAPPER) || !defined(XC_WIN32)
697 int findnearcolor(XColor *cvexact)
699 /* find the nearest-matching color in the colormap */
701 int i, ncolors = DisplayCells(dpy, DefaultScreen(dpy));
702 XColor *cmcolors;
703 long rdist, bdist, gdist;
704 u_long dist, mindist;
705 int minidx;
707 cmcolors = (XColor *)malloc(ncolors * sizeof(XColor));
709 for (i = 0; i < ncolors; i++) {
710 cmcolors[i].pixel = i;
711 cmcolors[i].flags = DoRed | DoGreen | DoBlue;
713 XQueryColors(dpy, cmap, cmcolors, ncolors);
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;
726 free(cmcolors);
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. */
733 /* findnearcolor already used 512 per component, so to match that, */
734 /* 3 * (512 * 512) = 786432 ~= 750000 */
736 if (dist > 750000) {
737 if (installowncmap() > 0) {
738 if (XAllocColor(dpy, cmap, cvexact) != 0)
739 minidx = cvexact->pixel;
743 return minidx;
746 #endif
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 /*----------------------------------------------------------------------*/
755 int xc_getlayoutcolor(int cidx)
757 return colorlist[cidx].color.pixel;
760 /*----------------------------------------------------------------------*/
761 /* Obtain red, green and blue values from a color index */
762 /* the colour values range from 0 to 65535 */
763 /*----------------------------------------------------------------------*/
765 void xc_get_color_rgb(unsigned long cidx, unsigned short *red,
766 unsigned short *green, unsigned short *blue)
768 XColor loccolor = {
769 .pixel = cidx,
770 .flags = DoRed | DoGreen | DoBlue
772 if (areawin->area == NULL) {
773 loccolor.red = cidx & 0xff;
774 loccolor.green = (cidx >> 8) & 0xff;
775 loccolor.blue = (cidx >> 16) & 0xff;
777 else
778 XQueryColors(dpy, cmap, &loccolor, 1);
779 *red = loccolor.red;
780 *green = loccolor.green;
781 *blue = loccolor.blue;
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 /*-------------------------------------------------------------------------*/
796 #if defined(TCL_WRAPPER) || !defined(XC_WIN32)
798 caddr_t CvtStringToPixel(XrmValuePtr args, int *nargs, XrmValuePtr fromVal,
799 XrmValuePtr toVal)
801 static XColor cvcolor;
802 XColor cvexact;
804 if (dpy == NULL) return NULL;
806 if (*nargs != 0)
807 Fprintf(stderr, "String to Pixel conversion takes no arguments");
809 /* Color defaults to black if name is not found */
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);
819 toVal->size = sizeof(u_long);
820 toVal->addr = (caddr_t) &(cvcolor.pixel);
821 return NULL;
824 #endif
826 /*-------------------------------------------------------------------------*/
827 /* Allocate new color using the CvtStringToPixel routine */
828 /*-------------------------------------------------------------------------*/
830 #if defined(XC_WIN32) && !defined(TCL_WRAPPER)
832 int xc_alloccolor(char *name)
834 return WinNamedColor(name);
837 #else
839 int xc_alloccolor(char *name)
841 XrmValue fromC, toC;
842 int zval = 0, pixval;
844 fromC.size = strlen(name);
845 fromC.addr = name;
847 if (areawin && (areawin->area)) {
848 CvtStringToPixel(NULL, &zval, &fromC, &toC);
849 pixval = (int)(*((u_long *)toC.addr));
851 else {
852 int r, g, b;
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;
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;
867 else if (sscanf(name, "%d", &pixval) != 1) {
868 if (!strcasecmp(name, "red")) {
869 r = 255;
870 g = 0;
871 b = 0;
873 else if (!strcasecmp(name, "green")) {
874 r = 0;
875 g = 255;
876 b = 0;
878 else if (!strcasecmp(name, "blue")) {
879 r = 0;
880 g = 0;
881 b = 255;
883 else if (!strcasecmp(name, "white")) {
884 r = 255;
885 g = 255;
886 b = 255;
888 else if (!strcasecmp(name, "gray")) {
889 r = 128;
890 g = 128;
891 b = 128;
893 else if (!strcasecmp(name, "yellow")) {
894 r = 255;
895 g = 255;
896 b = 0;
898 else if (!strcasecmp(name, "gray40")) {
899 r = 102;
900 g = 102;
901 b = 102;
903 else if (!strcasecmp(name, "gray60")) {
904 r = 153;
905 g = 153;
906 b = 153;
908 else if (!strcasecmp(name, "gray80")) {
909 r = 204;
910 g = 204;
911 b = 204;
913 else if (!strcasecmp(name, "gray90")) {
914 r = 229;
915 g = 229;
916 b = 229;
918 else if (!strcasecmp(name, "green2")) {
919 r = 0;
920 g = 238;
921 b = 0;
923 else if (!strcasecmp(name, "purple")) {
924 r = 160;
925 g = 32;
926 b = 240;
928 else if (!strcasecmp(name, "steelblue2")) {
929 r = 92;
930 g = 172;
931 b = 238;
933 else if (!strcasecmp(name, "red3")) {
934 r = 205;
935 g = 0;
936 b = 0;
938 else if (!strcasecmp(name, "tan")) {
939 r = 210;
940 g = 180;
941 b = 140;
943 else if (!strcasecmp(name, "brown")) {
944 r = 165;
945 g = 42;
946 b = 42;
948 else if (!strcasecmp(name, "pink")) {
949 r = 255;
950 g = 192;
951 b = 203;
953 else {
954 // Default to black
955 r = 0;
956 g = 0;
957 b = 0;
959 pixval = r + g * 256 + b * 65536;
963 return pixval;
966 #endif
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 /*----------------------------------------------------------------------*/
983 int rgb_querycolor(int red, int green, int blue, int *pixval)
985 int i;
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;
997 return ERRORCOLOR;
1000 /*-----------------------------------------------------------------------*/
1001 /* Allocate new color from RGB values (e.g., from PS file "scb" command) */
1002 /*-----------------------------------------------------------------------*/
1004 #if defined(XC_WIN32) && !defined(TCL_WRAPPER)
1006 int rgb_alloccolor(int red, int green, int blue)
1008 ERROR FIXME!
1009 // XCircuit version 3.9: This no longer works. Need to
1010 // allocate the color and return an index into colorlist[].
1012 return RGB(red >> 8, green >> 8, blue >> 8);
1015 #else
1017 int rgb_alloccolor(int red, int green, int blue)
1019 XColor newcolor;
1020 int pixval, tableidx; /* i, (jdk) */
1022 /* if color is not already in list, try to allocate it; if allocation */
1023 /* fails, grab the closest match in the colormap. */
1025 tableidx = rgb_querycolor(red, green, blue, &pixval);
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;
1038 else {
1039 // No graphics mode: Force pixel to be 24 bit RGB
1040 pixval = (red & 0xff) | ((blue & 0xff) << 8) | ((green & 0xff) << 16);
1043 // Add this to the colormap
1044 tableidx = addnewcolorentry(pixval);
1046 return tableidx;
1049 #endif
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 /*----------------------------------------------------------------------*/
1058 int query_named_color(char *cname)
1060 XColor cvcolor, cvexact;
1061 int tableidx, result = 0;
1063 if (areawin->area)
1064 result = XLookupColor(dpy, cmap, cname, &cvexact, &cvcolor);
1066 if (result == 0) return BADCOLOR;
1068 tableidx = rgb_querycolor(cvcolor.red, cvcolor.green, cvcolor.blue, NULL);
1069 return tableidx;
1072 /*-------------------------------------------------------------------------*/
1073 /* Make the cursors from the cursor bit data */
1074 /*-------------------------------------------------------------------------*/
1076 void makecursors()
1078 XColor fgcolor, bgcolor;
1079 Window win = areawin->window;
1080 #ifdef TCL_WRAPPER
1081 Tk_Uid fg_uid, bg_uid;
1082 #endif
1084 if (areawin->area == NULL) return;
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);
1091 #ifdef TCL_WRAPPER
1093 #ifdef XC_WIN32
1094 #define Tk_GetCursorFromData CreateW32Cursor
1095 #endif
1097 fg_uid = Tk_GetUid(Tk_NameOfColor(&fgcolor));
1098 bg_uid = Tk_GetUid(Tk_NameOfColor(&bgcolor));
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);
1128 #ifdef XC_WIN32
1129 #undef Tk_GetCursorFromData
1130 #endif
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
1164 TEXTPTR = XCreateFontCursor(dpy, XC_xterm);
1165 WAITFOR = XCreateFontCursor(dpy, XC_watch);
1167 XRecolorCursor(dpy, TEXTPTR, &fgcolor, &bgcolor);
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 /*----------------------------------------------------------------------*/
1175 void delete_window(XCWindowData *window)
1177 XCWindowData *searchwin, *lastwin = NULL;
1179 if (xobjs.windowlist->next == NULL) {
1180 quitcheck((window == NULL) ? NULL : window->area, NULL, NULL);
1181 return;
1184 for (searchwin = xobjs.windowlist; searchwin != NULL; searchwin =
1185 searchwin->next) {
1186 if (searchwin == window) {
1187 Matrixptr thismat;
1189 /* Free any select list */
1190 if (searchwin->selects > 0) free(searchwin->selectlist);
1192 /* Free the matrix and pushlist stacks */
1194 while (searchwin->MatStack != NULL) {
1195 thismat = searchwin->MatStack;
1196 searchwin->MatStack = searchwin->MatStack->nextmatrix;
1197 free(thismat);
1199 free_stack(&searchwin->hierstack);
1200 free_stack(&searchwin->stack);
1202 /* Free the GC */
1203 XFreeGC(dpy, searchwin->gc);
1205 if (lastwin != NULL)
1206 lastwin->next = searchwin->next;
1207 else
1208 xobjs.windowlist = searchwin->next;
1209 break;
1211 lastwin = searchwin;
1214 if (searchwin == NULL) {
1215 Wprintf("No such window in list!\n");
1217 else {
1218 if (areawin == searchwin) areawin = xobjs.windowlist;
1219 free(searchwin);
1223 /*----------------------------------------------------------------------*/
1224 /* Create a new window structure and initialize it. */
1225 /* Return a pointer to the new window. */
1226 /*----------------------------------------------------------------------*/
1228 XCWindowData *create_new_window()
1230 XCWindowData *newwindow;
1232 newwindow = (XCWindowData *)malloc(sizeof(XCWindowData));
1234 #ifndef TCL_WRAPPER
1235 #ifdef HAVE_XPM
1236 newwindow->toolbar_on = True;
1237 #endif
1238 #endif
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;
1296 /* Prepend to linked window list in global data (xobjs) */
1297 newwindow->next = xobjs.windowlist;
1298 xobjs.windowlist = newwindow;
1300 return newwindow;
1303 /*----------------------------------------------------------------------*/
1304 /* Preparatory initialization (to be run before setting up the GUI) */
1305 /*----------------------------------------------------------------------*/
1307 void pre_initialize()
1309 short i, page;
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 /*-------------------------------------------------------------*/
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");
1329 /*---------------------------*/
1330 /* initialize user variables */
1331 /*---------------------------*/
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;
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;
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;
1366 xobjs.technologies = NULL;
1367 xobjs.undostack = NULL;
1368 xobjs.redostack = NULL;
1370 /* Set the temporary directory name as compiled, unless overridden by */
1371 /* environment variable "TMPDIR". */
1373 xobjs.tempdir = getenv("TMPDIR");
1374 if (xobjs.tempdir == NULL) xobjs.tempdir = strdup(TEMP_DIR);
1376 xobjs.windowlist = (XCWindowDataPtr)NULL;
1377 areawin = NULL;
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;
1387 xobjs.imagelist = NULL;
1388 xobjs.images = 0;
1389 xobjs.pages = PAGES;
1391 xobjs.libsearchpath = (char *)NULL;
1392 xobjs.filesearchpath = (char *)NULL;
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;
1400 /* Initialization of objects requires values for the window width and height, */
1401 /* so set up the widgets and realize them first. */
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 */
1409 #ifdef TCL_WRAPPER
1411 /*----------------------------------------------------------------------*/
1412 /* Create a new Handle object in Tcl */
1413 /*----------------------------------------------------------------------*/
1415 static void UpdateStringOfHandle _ANSI_ARGS_((Tcl_Obj *objPtr));
1416 static int SetHandleFromAny _ANSI_ARGS_((Tcl_Interp *interp, Tcl_Obj *objPtr));
1418 static Tcl_ObjType tclHandleType = {
1419 "handle", /* name */
1420 (Tcl_FreeInternalRepProc *) NULL, /* freeIntRepProc */
1421 (Tcl_DupInternalRepProc *) NULL, /* dupIntRepProc */
1422 UpdateStringOfHandle, /* updateStringProc */
1423 SetHandleFromAny /* setFromAnyProc */
1426 /*----------------------------------------------------------------------*/
1428 static void
1429 UpdateStringOfHandle(objPtr)
1430 Tcl_Obj *objPtr; /* Int object whose string rep to update. */
1432 char buffer[TCL_INTEGER_SPACE];
1433 int len;
1435 sprintf(buffer, "H%08lX", objPtr->internalRep.longValue);
1436 len = strlen(buffer);
1438 objPtr->bytes = Tcl_Alloc((u_int)len + 1);
1439 strcpy(objPtr->bytes, buffer);
1440 objPtr->length = len;
1443 /*----------------------------------------------------------------------*/
1445 static int
1446 SetHandleFromAny(interp, objPtr)
1447 Tcl_Interp *interp; /* Used for error reporting if not NULL. */
1448 Tcl_Obj *objPtr; /* The object to convert. */
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;
1457 string = Tcl_GetStringFromObj(objPtr, &length);
1458 errno = 0;
1459 for (p = string; isspace((u_char)(*p)); p++);
1461 nexthier:
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);
1469 free_stack(&newstack);
1470 return TCL_ERROR;
1471 } else {
1472 newLong = strtoul(p, &end, 16);
1474 if (end == p) {
1475 badHandle:
1476 if (interp != NULL) {
1478 * Must copy string before resetting the result in case a caller
1479 * is trying to convert the interpreter's result to an int.
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);
1487 free_stack(&newstack);
1488 return TCL_ERROR;
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);
1497 free_stack(&newstack);
1498 return TCL_ERROR;
1501 * Make sure that the string has no garbage after the end of the handle.
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. */
1509 if ((end != NULL) && (*end == '/')) {
1510 objinstptr refinst, chkinst;
1511 genericptr *rgen;
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;
1530 break;
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;
1539 push_stack(&newstack, chkinst, NULL);
1540 goto nexthier;
1542 else
1543 goto badHandle;
1546 /* Note that this check won't prevent a hierarchical selection from */
1547 /* being added to a non-hierarchical selection. */
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;
1558 free_stack(&areawin->hierstack);
1559 areawin->hierstack = newstack;
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.
1568 if ((oldTypePtr != NULL) && (oldTypePtr->freeIntRepProc != NULL)) {
1569 oldTypePtr->freeIntRepProc(objPtr);
1572 objPtr->internalRep.longValue = newLong;
1573 objPtr->typePtr = &tclHandleType;
1574 return TCL_OK;
1577 /*----------------------------------------------------------------------*/
1579 Tcl_Obj *
1580 Tcl_NewHandleObj(optr)
1581 void *optr; /* Int used to initialize the new object. */
1583 Tcl_Obj *objPtr;
1585 objPtr = Tcl_NewObj();
1586 objPtr->bytes = NULL;
1588 objPtr->internalRep.longValue = (long)(optr);
1589 objPtr->typePtr = &tclHandleType;
1590 return objPtr;
1593 /*----------------------------------------------------------------------*/
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. */
1601 long l;
1602 int result;
1604 if (objPtr->typePtr != &tclHandleType) {
1605 result = SetHandleFromAny(interp, objPtr);
1606 if (result != TCL_OK) {
1607 return result;
1610 l = objPtr->internalRep.longValue;
1612 #if SIZEOF_VOID_P == SIZEOF_UNSIGNED_INT
1613 if (((long)((int)l)) == l) {
1614 *handlePtr = (void *)objPtr->internalRep.longValue;
1615 return TCL_OK;
1617 #else
1618 #if SIZEOF_VOID_P == SIZEOF_UNSIGNED_LONG
1619 *handlePtr = (void *)objPtr->internalRep.longValue;
1620 return TCL_OK;
1621 #endif
1622 #endif
1624 if (interp != NULL) {
1625 Tcl_ResetResult(interp);
1626 Tcl_AppendToObj(Tcl_GetObjResult(interp),
1627 "value too large to represent as handle", -1);
1629 return TCL_ERROR;
1633 #endif
1635 /*----------------------------------------------------------------------*/
1636 /* Routine to initialize variables after the GUI has been set up */
1637 /*----------------------------------------------------------------------*/
1639 void post_initialize()
1641 short i;
1643 /*--------------------------------------------------*/
1644 /* Setup the (simple) colormap and make the cursors */
1645 /*--------------------------------------------------*/
1647 setcolorscheme(True);
1648 makecursors();
1650 /* Now that we have values for the window width and height, we can initialize */
1651 /* the page objects. */
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);
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);
1667 changepage(0);
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. */
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
1679 /* Set up fundamentally necessary colors black and white */
1681 addnewcolorentry(xc_alloccolor("Black"));
1682 addnewcolorentry(xc_alloccolor("White"));
1684 #ifdef TCL_WRAPPER
1686 /* Set up new Tcl type "handle" for element handles */
1687 Tcl_RegisterObjType(&tclHandleType);
1689 #endif
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 /*-----------------------------------------------------*/
1697 if (areawin->area != NULL) {
1698 XDefineCursor (dpy, areawin->window, DEFAULTCURSOR);
1700 /*---------------------------------------------------*/
1701 /* Set up a timeout for automatic save to a tempfile */
1702 /*---------------------------------------------------*/
1704 xobjs.save_interval = appdata.timeout;
1705 xobjs.timeout_id = xcAddTimeOut(app, 60000 * xobjs.save_interval,
1706 savetemp, NULL);
1710 /*----------------------------------------------------------------------*/