/* misc handy X Windows functions. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include "xephem.h" static void stopd_cb (Widget w, XtPointer data, XtPointer call); static Widget stopd_w; /* dialog for user-stop */ static int stopd_stopped; /* flag set by the stopd PB */ #define MAXGRAY 30 /* max colors in a grayscale ramp */ /* createFSM aux info */ typedef struct { char **suffixes; /* malloced list of suffixes */ int nsuffixes; /* number of suffixes */ char *sharedir; /* dir within ShareDir to scan */ XtCallbackProc cb; /* callback for each PB */ } FSMInfo; /* names of resource colors for the planets. * N.B. must be in the same order as the defines in astro.h */ static char *plcolnames[] = { "mercuryColor", "venusColor", "marsColor", "jupiterColor", "saturnColor", "uranusColor", "neptuneColor", "plutoColor", "sunColor", "moonColor" }; /* name of resource for all other solar system objects */ static char solsyscolname[] = "solSysColor"; /* spectral colors are defined with X res names starSpectX or starSpectXX. * resource is looked up first pick is requested */ static char sresbase[] = "starSpect"; /* base name for all spect res names */ typedef struct { char class[2]; /* 1 or 2 char spec class */ GC gc; /* gc to use */ } SpColor; static SpColor *spcolors; /* malloced array as discovered */ static int nspcolors; /* entries in spcolors[] */ /* font and GCs we manage */ static XFontStruct *viewsfsp; static XFontStruct *trackingfsp; static GC pl_gc[XtNumber(plcolnames)]; static GC solsys_gc; static GC esat_gc; static GC other_gc; static GC otherstar_gc; /* info used to make XmButton look like XmButton or XmLabel */ static Arg look_like_button[] = { {XmNtopShadowColor, (XtArgVal) 0}, {XmNbottomShadowColor, (XtArgVal) 0}, {XmNtopShadowPixmap, (XtArgVal) 0}, {XmNbottomShadowPixmap, (XtArgVal) 0}, {XmNfillOnArm, (XtArgVal) True}, {XmNtraversalOn, (XtArgVal) True}, }; static Arg look_like_label[] = { {XmNtopShadowColor, (XtArgVal) 0}, {XmNbottomShadowColor, (XtArgVal) 0}, {XmNtopShadowPixmap, (XtArgVal) 0}, {XmNbottomShadowPixmap, (XtArgVal) 0}, {XmNfillOnArm, (XtArgVal) False}, {XmNtraversalOn, (XtArgVal) False}, }; static int look_like_inited; /* handy way to set one resource for a widget. * shouldn't use this if you have several things to set for the same widget. */ void set_something (w, resource, value) Widget w; char *resource; XtArgVal value; { Arg a[1]; if (!w) { printf ("set_something(w=%p, res=%s)\n", w, resource); abort(); } XtSetArg (a[0], resource, value); XtSetValues (w, a, 1); } /* handy way to get one resource for a widget. * shouldn't use this if you have several things to get for the same widget. */ void get_something (w, resource, value) Widget w; char *resource; XtArgVal value; { Arg a[1]; if (!w) { printf ("get_something (%s) called with w==0\n", resource); abort(); } XtSetArg (a[0], resource, value); XtGetValues (w, a, 1); } /* return the given XmString resource from the given widget as a char *. * N.B. based on a sample in Heller, pg 178, the string back from * XmStringGetLtoR should be XtFree'd. Therefore, OUR caller should always * XtFree (*txtp). */ void get_xmstring (w, resource, txtp) Widget w; char *resource; char **txtp; { static char me[] = "get_xmstring()"; static char hah[] = "??"; if (!w) { printf ("%s: called for %s with w==0\n", me, resource); abort(); } else { XmString str; get_something(w, resource, (XtArgVal)&str); if (!XmStringGetLtoR (str, XmSTRING_DEFAULT_CHARSET, txtp)) { /* fprintf (stderr, "%s: can't get string resource %s\n", me, resource); abort(); */ (void) strcpy (*txtp = XtMalloc(sizeof(hah)), hah); } XmStringFree (str); } } void set_xmstring (w, resource, txt) Widget w; char *resource; char *txt; { XmString str; if (!w) { printf ("set_xmstring(w=%p, res=%s, txt=%s)\n", w, resource, txt); abort(); } str = XmStringCreateLtoR (txt, XmSTRING_DEFAULT_CHARSET); set_something (w, resource, (XtArgVal)str); XmStringFree (str); } /* return 1 if w is on screen else 0 */ int isUp (w) Widget w; { XWindowAttributes wa; Display *dsp; Window win; if (!w) return (0); dsp = XtDisplay(w); win = XtWindow(w); return (win && XGetWindowAttributes(dsp, win, &wa) && wa.map_state == IsViewable); } /* may be connected as the mapCallback or XmNpopupCallback to * center a window on the cursor (allowing for the screen edges). */ /* ARGSUSED */ void prompt_map_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { Window root, child; int rx, ry, wx, wy; /* rx/y: cursor loc on root window */ unsigned sw, sh; /* screen width/height */ Dimension ww, wh; /* this widget's width/height */ Position x, y; /* final location */ unsigned mask; Arg args[20]; int n; XQueryPointer (XtDisplay(w), XtWindow(w), &root, &child, &rx, &ry, &wx, &wy, &mask); sw = WidthOfScreen (XtScreen(w)); sh = HeightOfScreen(XtScreen(w)); n = 0; XtSetArg (args[n], XmNwidth, &ww); n++; XtSetArg (args[n], XmNheight, &wh); n++; XtGetValues (w, args, n); x = rx - ww/2; if (x < 0) x = 0; else if (x + ww >= (int)sw) x = sw - ww; y = ry - wh/2; if (y < 0) y = 0; else if (y + wh >= (int)sh) y = sh - wh; n = 0; XtSetArg (args[n], XmNx, x); n++; XtSetArg (args[n], XmNy, y); n++; XtSetValues (w, args, n); } /* get the named color for w's colormap in *p, else set to White. * return 0 if the color was found, -1 if White had to be used. */ int get_color_resource (w, cname, p) Widget w; char *cname; Pixel *p; { Display *dsp = XtDisplay(w); Colormap cm; XColor defxc, dbxc; Arg arg; char *cval; XtSetArg (arg, XmNcolormap, &cm); XtGetValues (w, &arg, 1); cval = getXRes (cname, NULL); if (!cval || !XAllocNamedColor (dsp, cm, cval, &defxc, &dbxc)) { char msg[128]; if (!cval) sprintf (msg, "Can not find resource `%.80s'", cname); else sprintf (msg, "Can not XAlloc color `%.80s'", cval); strcat (msg, " ... using White"); xe_msg (0, msg); *p = WhitePixel (dsp, DefaultScreen(dsp)); return (-1); } else { *p = defxc.pixel; return (0); } } /* get the XFontStruct we want to use when drawing text for the display views. */ void get_views_font (dsp, fspp) Display *dsp; XFontStruct **fspp; { if (!viewsfsp) viewsfsp = getXResFont ("viewsFont"); *fspp = viewsfsp; } /* set the XFontStruct we want to use when drawing text for the display views. * this also means setting all the GCs for objects. */ void set_views_font (dsp, fsp) Display *dsp; XFontStruct *fsp; { Font fid; int i; /* TODO: free old? */ viewsfsp = fsp; /* may not have created gcs yet */ if (!other_gc) make_objgcs(); /* update all Fonts in GCs */ fid = fsp->fid; for (i = 0; i < XtNumber(plcolnames); i++) XSetFont (dsp, pl_gc[i], fid); for (i = 0; i < nspcolors; i++) XSetFont (dsp, spcolors[i].gc, fid); XSetFont (dsp, solsys_gc, fid); XSetFont (dsp, other_gc, fid); XSetFont (dsp, otherstar_gc, fid); XSetFont (dsp, esat_gc, fid); } /* get the XFontStruct we want to use when drawing text while tracking cursor. */ void get_tracking_font (dsp, fspp) Display *dsp; XFontStruct **fspp; { if (!trackingfsp) trackingfsp = getXResFont ("cursorTrackingFont"); *fspp = trackingfsp; } /* set the XFontStruct we want to use when drawing text while tracking cursor. */ void set_tracking_font (dsp, fsp) Display *dsp; XFontStruct *fsp; { /* TODO: free old? */ trackingfsp = fsp; } /* make all the various GCs used for objects from obj_pickgc(). * TODO: reclaim old stuff if called again, but beware of hoarding users. */ void make_objgcs() { Display *dsp = XtDisplay(toplevel_w); Window win = RootWindow (dsp, DefaultScreen (dsp)); unsigned long gcm; XFontStruct *fsp; XGCValues gcv; Pixel p; int i, j; /* always set font and foreground */ gcm = GCFont | GCForeground; get_views_font (dsp, &fsp); gcv.font = fsp->fid; /* make the planet gcs */ for (i = 0; i < XtNumber(pl_gc); i++) { (void) get_color_resource (toplevel_w, plcolnames[i], &p); gcv.foreground = p; pl_gc[i] = XCreateGC (dsp, win, gcm, &gcv); } /* make the gc for other solar system objects */ (void) get_color_resource (toplevel_w, solsyscolname, &p); gcv.foreground = p; solsys_gc = XCreateGC (dsp, win, gcm, &gcv); /* make the gc for fixed types other than stars */ (void) get_color_resource (toplevel_w, "otherObjColor", &p); gcv.foreground = p; other_gc = XCreateGC (dsp, win, gcm, &gcv); /* make the gc for stars without a matching spectral color */ gcv.foreground = WhitePixel (dsp, DefaultScreen(dsp)); otherstar_gc = XCreateGC (dsp, win, gcm, &gcv); /* build all spectral colors in resource database */ for (i = 0; i < nspcolors; i++) XFreeGC (dsp, spcolors[i].gc); nspcolors = 0; for (i = 'A'; i <= 'Z'; i++) { for (j = 0; j <= '9'; j = j ? j+1 : '0') { char *v, res[sizeof(sresbase)+3]; SpColor *sp; sprintf (res, "%s%c%c", sresbase, i, j); v = getXRes (res, NULL); if (v) { XColor defxc, dbxc; if (!XAllocNamedColor (dsp, xe_cm, v, &defxc, &dbxc)) defxc.pixel = WhitePixel (dsp, DefaultScreen(dsp)); spcolors = (SpColor *) XtRealloc ((char *)spcolors, (nspcolors+1)*sizeof(SpColor)); sp = &spcolors[nspcolors++]; gcv.foreground = defxc.pixel; sp->gc = XCreateGC (dsp, win, gcm, &gcv); sp->class[0] = i; sp->class[1] = j; } } } /* make the gc for earth sats */ (void) get_color_resource (toplevel_w, "satellitesColor", &p); gcv.foreground = p; esat_gc = XCreateGC (dsp, win, gcm, &gcv); } /* given an object, return a GC for it. * Use the colors defined for objects in the X resources, else White. */ void obj_pickgc(op, w, gcp) Obj *op; Widget w; GC *gcp; { /* insure GCs are ready */ if (!other_gc) make_objgcs(); if (is_type (op, PLANETM)) *gcp = pl_gc[op->pl_code]; /* moons share parent color */ else if (is_ssobj(op)) *gcp = solsys_gc; else if (is_type (op, EARTHSATM)) *gcp = esat_gc; else if (is_type (op, FIXEDM) || is_type (op, BINARYSTARM)) { int l, u, m = 0, d = -1; /* all but bonafide stars use "other" */ if (is_type (op, FIXEDM)) { switch (op->f_class) { case 'T': case 'B': case 'D': case 'M': case 'S': case 'V': break; default: *gcp = other_gc; return; } } /* binary search for perfect match */ l = 0; u = nspcolors - 1; while (l <= u) { m = (l+u)/2; d = strncmp (spcolors[m].class, op->f_spect, 2); if (d == 0) break; if (d < 0) l = m+1; else u = m-1; } /* select perfect or +/- 1 if same first char */ if (d == 0 || op->f_spect[0] == spcolors[m].class[0]) *gcp = spcolors[m].gc; else if (m > 0 && op->f_spect[0] == spcolors[m-1].class[0]) *gcp = spcolors[m-1].gc; else if (m< nspcolors-1 && op->f_spect[0] == spcolors[m+1].class[0]) *gcp = spcolors[m+1].gc; else { if (op->f_spect[0]) { xe_msg (0, "No color specified for %s class %c spectral class %.2s", op->o_name, op->f_class, op->f_spect); } *gcp = otherstar_gc; } } else { printf ("Unknown object type %d for coloring\n", op->o_type); abort(); } } /* given any widget built from an XmLabel return pointer to the first * XFontStruct in its XmFontList. */ void get_xmlabel_font (w, f) Widget w; XFontStruct **f; { static char me[] = "get_xmlable_font"; XmFontList fl; XmFontContext fc; XmStringCharSet charset; get_something (w, XmNfontList, (XtArgVal)&fl); if (XmFontListInitFontContext (&fc, fl) != True) { printf ("%s: No Font context!\n", me); abort(); } if (XmFontListGetNextFont (fc, &charset, f) != True) { printf ("%s: no font!\n", me); abort(); } XmFontListFreeFontContext (fc); } /* get the font named by the given X resource, else fixed, else bust */ XFontStruct * getXResFont (rn) char *rn; { static char fixed[] = "fixed"; char *fn = getXRes (rn, NULL); Display *dsp = XtDisplay(toplevel_w); XFontStruct *fsp; if (!fn) { xe_msg (0, "No resource `%s' .. using fixed", rn); fn = fixed; } /* use XLoadQueryFont because it returns gracefully if font is not * found; XLoadFont calls the default X error handler. */ fsp = XLoadQueryFont (dsp, fn); if (!fsp) { xe_msg (0, "No font `%s' for `%s' .. using fixed", fn,rn); fsp = XLoadQueryFont (dsp, fixed); if (!fsp) { printf ("Can't even get %s!\n", fixed); abort(); } } return (fsp); } /* load the greek font into *greekfspp then create a new gc at *greekgcp and * set its font to the font id.. * leave *greekgcp and greekfssp unchanged if there's any problems. */ void loadGreek (Display *dsp, Drawable win, GC *greekgcp, XFontStruct **greekfspp) { static char grres[] = "viewsGreekFont"; XFontStruct *fsp; /* local fast access */ GC ggc; /* local fast access */ unsigned long gcm; XGCValues gcv; char *greekfn; greekfn = getXRes (grres, NULL); if (!greekfn) { xe_msg (0, "No resource: %s", grres); return; } fsp = XLoadQueryFont (dsp, greekfn); if (!fsp) { xe_msg (0, "No font %.100s: %.800s", grres, greekfn); return; } gcm = GCFont; gcv.font = fsp->fid; ggc = XCreateGC (dsp, win, gcm, &gcv); if (!ggc) { XFreeFont (dsp, fsp); xe_msg (0, "Can not make Greek GC"); return; } *greekgcp = ggc; *greekfspp = fsp; return; } /* return a gray-scale ramp of pixels at *pixp, and the number in the ramp * N.B. don't change the pixels -- they are shared with other users. */ int gray_ramp (dsp, cm, pixp) Display *dsp; Colormap cm; Pixel **pixp; { static Pixel gramp[MAXGRAY]; static int ngray; if (ngray == 0) { /* get gray ramp pixels once */ XColor white; white.red = white.green = white.blue = ~0; ngray = alloc_ramp (dsp, &white, cm, gramp, MAXGRAY); if (ngray < MAXGRAY) xe_msg (0, "Wanted %d but only found %d grays.",MAXGRAY,ngray); } *pixp = gramp; return (ngray); } /* try to fill pix[maxn] with linear ramp from black to whatever is in base. * each entry will be unique; return said number, which can be <= maxn. * N.B. if we end up with just 2 colors, we set pix[0]=0 and pix[1]=1 in * anticipation of caller using a XYBitmap and thus XPutImage for which * color 0/1 uses the background/foreground of a GC. */ int alloc_ramp (Display *dsp, XColor *basep, Colormap cm, Pixel pix[], int maxn) { int nalloc, nunique; double r, g, b, h, s, v; XColor xc; int i, j; /* work in HSV space from 0..V */ r = basep->red/65535.; g = basep->green/65535.; b = basep->blue/65535.; toHSV (r, g, b, &h, &s, &v); /* first just try to get them all */ for (nalloc = 0; nalloc < maxn; nalloc++) { toRGB (h, s, v*nalloc/(maxn-1), &r, &g, &b); xc.red = (int)(r*65535); xc.green = (int)(g*65535); xc.blue = (int)(b*65535); if (XAllocColor (dsp, cm, &xc)) pix[nalloc] = xc.pixel; else break; } /* see how many are actually unique */ nunique = 0; for (i = 0; i < nalloc; i++) { for (j = i+1; j < nalloc; j++) if (pix[i] == pix[j]) break; if (j == nalloc) nunique++; } if (nunique < maxn) { /* rebuild the ramp using just nunique entries. * N.B. we assume we can get nunique colors again right away. */ XFreeColors (dsp, cm, pix, nalloc, 0); if (nunique <= 2) { /* we expect caller to use a XYBitmap via GC */ pix[0] = 0; pix[1] = 1; nunique = 2; } else { for (i = 0; i < nunique; i++) { toRGB (h, s, v*i/(nunique-1), &r, &g, &b); xc.red = (int)(r*65535); xc.green = (int)(g*65535); xc.blue = (int)(b*65535); if (!XAllocColor (dsp, cm, &xc)) { nunique = i; break; } pix[i] = xc.pixel; } } } return (nunique); } /* create an XImage of size wXh. * return XImage * if ok else NULL and xe_msg(). */ XImage * create_xim (int w, int h) { Display *dsp = XtDisplay(toplevel_w); XImage *xip; int mdepth; int mbpp; int nbytes; char *data; /* establish depth and bits per pixel */ get_something (toplevel_w, XmNdepth, (XtArgVal)&mdepth); if (mdepth < 8) { fprintf (stderr, "Require at least 8 bit pixel depth\n"); return (NULL); } mbpp = mdepth>=17 ? 32 : (mdepth >= 9 ? 16 : 8); nbytes = w*h*mbpp/8; /* get memory for image pixels. */ data = malloc (nbytes); if (!data) { fprintf(stderr,"Can not get %d bytes for image pixels", nbytes); return (NULL); } /* create the XImage */ xip = XCreateImage (dsp, DefaultVisual (dsp, DefaultScreen(dsp)), /* depth */ mdepth, /* format */ ZPixmap, /* offset */ 0, /* data */ data, /* width */ w, /* height */ h, /* pad */ mbpp, /* bpl */ 0); if (!xip) { fprintf (stderr, "Can not create %dx%d XImage\n", w, h); free ((void *)data); return (NULL); } xip->bitmap_bit_order = LSBFirst; xip->byte_order = LSBFirst; /* ok */ return (xip); } /* like XFreeColors but frees the pixels in xcols[nxcols] */ void freeXColors (Display *dsp, Colormap cm, XColor xcols[], int nxcols) { unsigned long *xpix = (Pixel*)XtMalloc(nxcols * sizeof(unsigned long)); int i; for (i = 0; i < nxcols; i++) xpix[i] = xcols[i].pixel; XFreeColors (dsp, cm, xpix, nxcols, 0); XtFree ((void *)xpix); } /* given a raw (still compressed) gif file in gif[ngif], malloc its 1-byte * pixels in *gifpix[*wp][*hp] and fill xcols[256] with X pixels and colors * that work when indexed by an gifpix entry. * return 0 if ok, else -1 with err[] containing a reason why not. */ int gif2X ( Display *dsp, /* X server */ Colormap cm, /* colormap for xcols[] */ unsigned char gif[], /* raw (still compressed) gif file contents */ int ngif, /* bytes in gif[] */ int *wp, int *hp, /* size of exploded gif */ unsigned char **gifpix, /* ptr to array we malloc for gif pixels */ XColor xcols[256], /* X pixels and colors when indexed by gifpix */ char err[]) /* error message if we return -1 */ { unsigned char gifr[256], gifg[256], gifb[256]; int i; /* uncompress */ if (explodeGIF(gif, ngif, wp, hp, gifpix, gifr, gifg, gifb, err) < 0) return (-1); /* allocate colors -- don't be too fussy */ for (i = 0; i < 256; i++) { XColor *xcp = xcols+i; xcp->red = ((short)(gifr[i] & 0xf8) << 8) | 0x7ff; xcp->green = ((short)(gifg[i] & 0xf8) << 8) | 0x7ff; xcp->blue = ((short)(gifb[i] & 0xf8) << 8) | 0x7ff; if (!XAllocColor (dsp, cm, xcp)) { strcpy (err, "Can not get all image map colors"); free ((void *)(*gifpix)); if (i > 0) freeXColors (dsp, cm, xcols, i); return (-1); } } /* ok */ return (0); } /* given a raw gif file in gif[ngif] return a new pixmap and its size. * return 0 if ok, else fill why[] and return -1. */ int gif2pm (Display *dsp, Colormap cm, unsigned char gif[], int ngif, int *wp, int *hp, Pixmap *pmp, char why[]) { Window win = RootWindow(dsp, DefaultScreen(dsp)); unsigned char *gifpix; XColor xcols[256]; XImage *xip; Pixmap pm; GC gc; int w, h; int x, y; /* get X version of image */ if (gif2X (dsp, cm, gif, ngif, &w, &h, &gifpix, xcols, why) < 0) return (-1); /* create XImage */ xip = create_xim (w, h); if (!xip) { freeXColors (dsp, cm, xcols, 256); free ((void *)gifpix); strcpy (why, "No memory for image"); return (-1); } /* N.B. now obligued to free xip */ /* fill XImage with image */ for (y = 0; y < h; y++) { int yrow = y*w; for (x = 0; x < w; x++) { int gp = (int)gifpix[x + yrow]; unsigned long p = xcols[gp].pixel; XPutPixel (xip, x, y, p); } } /* create pixmap and fill with image */ pm = XCreatePixmap (dsp, win, w, h, xip->depth); gc = DefaultGC (dsp, DefaultScreen(dsp)); XPutImage (dsp, pm, gc, xip, 0, 0, 0, 0, w, h); /* free gifpix and xip */ free ((void *)gifpix); free ((void *)xip->data); xip->data = NULL; XDestroyImage (xip); /* that's it! */ *wp = w; *hp = h; *pmp = pm; return (0); } /* search for the named X resource from all the usual places. * this looks in more places than XGetDefault(). * we just return it as a string -- caller can do whatever. * return def if can't find it anywhere. * N.B. memory returned is _not_ malloced so leave it be. * N.B. see setXRes for how newlines are handled in the Xrm. */ char * getXRes (name, def) char *name; char *def; { static char notfound[] = "_Not_Found_"; char *res = NULL; XtResource xr; xr.resource_name = name; xr.resource_class = "AnyClass"; xr.resource_type = XmRString; xr.resource_size = sizeof(String); xr.resource_offset = 0; xr.default_type = XmRImmediate; xr.default_addr = (XtPointer)notfound; XtGetApplicationResources (toplevel_w, (void *)&res, &xr, 1, NULL, 0); if (!res || strcmp (res, notfound) == 0) res = def; return (res); } /* set the given application (ie, myclass.name) resource. * N.B. the combination of setXRes/getXRes has the effect that each backslash-n * in val comes back as a real nl and everything including and after the * the first real nl is discarded. if you think about it, this is the same * behavior as writing to and reading back one res to an app-defaults file. */ void setXRes (name, val) char *name, *val; { XrmDatabase db = XrmGetDatabase (XtDisplay(toplevel_w)); char buf[1024]; sprintf (buf, "%s.%s:%s", myclass, name, val ? val : ""); XrmPutLineResource (&db, buf); } /* build and return a private colormap for toplevel_w. * nnew is how many colors we expect to add. */ Colormap createCM(nnew) int nnew; { #define NPRECM 50 /* try to preload new cm with NPRECM colors from def cm */ Display *dsp = XtDisplay (toplevel_w); Window win = RootWindow (dsp, DefaultScreen(dsp)); Colormap defcm = DefaultColormap (dsp, DefaultScreen(dsp)); int defcells = DisplayCells (dsp, DefaultScreen(dsp)); Visual *v = DefaultVisual (dsp, DefaultScreen(dsp)); Colormap newcm; /* make a new colormap */ newcm = XCreateColormap (dsp, win, v, AllocNone); /* preload with some existing colors to hedge flashing, if room */ if (nnew + NPRECM < defcells) { XColor preload[NPRECM]; int i; for (i = 0; i < NPRECM; i++) preload[i].pixel = (unsigned long) i; XQueryColors (dsp, defcm, preload, NPRECM); for (i = 0; i < NPRECM; i++) (void) XAllocColor (dsp, newcm, &preload[i]); } return (newcm); } /* depending on the "install" resource and whether cm can hold nwant more * colors, return a new colormap or cm again. */ Colormap checkCM(cm, nwant) Colormap cm; int nwant; { Display *dsp = XtDisplay(toplevel_w); char *inst; /* get the install resource value */ inst = getXRes ("install", "guess"); /* check each possible value */ if (strcmp (inst, "no") == 0) return (cm); else if (strcmp (inst, "yes") == 0) return (createCM (nwant)); else if (strcmp (inst, "guess") == 0) { /* get a smattering of colors and opt for private cm if can't. * we use alloc_ramp() because it verifies unique pixels. * we use three to not overstress the resolution of colors. */ Pixel *rr, *gr, *br; int neach, nr, ng, nb; XColor xc; /* grab some to test */ neach = nwant/3; xc.red = 255 << 8; xc.green = 0; xc.blue = 0; rr = (Pixel *) malloc (neach * sizeof(Pixel)); nr = alloc_ramp (dsp, &xc, cm, rr, neach); xc.red = 0; xc.green = 255 << 8; xc.blue = 0; gr = (Pixel *) malloc (neach * sizeof(Pixel)); ng = alloc_ramp (dsp, &xc, cm, gr, neach); xc.red = 0; xc.green = 0; xc.blue = 255 << 8; br = (Pixel *) malloc (neach * sizeof(Pixel)); nb = alloc_ramp (dsp, &xc, cm, br, neach); /* but don't keep them. * N.B. alloc_ramp just cheats us with B&W if it returns 2. */ if (nr > 2) XFreeColors (dsp, cm, rr, nr, 0); if (ng > 2) XFreeColors (dsp, cm, gr, ng, 0); if (nb > 2) XFreeColors (dsp, cm, br, nb, 0); free ((void *)rr); free ((void *)gr); free ((void *)br); if (nr + ng + nb < 3*neach) return (createCM(nwant)); } else printf ("Unknown install `%s' -- defaulting to No\n", inst); return (cm); } /* explicitly handle pending X events when otherwise too busy */ void XCheck (app) XtAppContext app; { while ((XtAppPending (app) & XtIMXEvent) == XtIMXEvent) XtAppProcessEvent (app, XtIMXEvent); } /* center the scrollbars in the given scrolled window */ void centerScrollBars(sw_w) Widget sw_w; { int min, max, slidersize, value; XmScrollBarCallbackStruct sbcs; Widget sb_w; /* you would think setting XmNvalue would be enough but seems we * must trigger the callback too, at least on MKS */ memset (&sbcs, 0, sizeof(sbcs)); sbcs.reason = XmCR_VALUE_CHANGED; sbcs.event = NULL; /* ? */ get_something (sw_w, XmNhorizontalScrollBar, (XtArgVal)&sb_w); get_something (sb_w, XmNminimum, (XtArgVal)&min); get_something (sb_w, XmNmaximum, (XtArgVal)&max); get_something (sb_w, XmNsliderSize, (XtArgVal)&slidersize); sbcs.value = value = (min+max-slidersize)/2; set_something (sb_w, XmNvalue, (XtArgVal)value); XtCallCallbacks (sb_w, XmNvalueChangedCallback, &sbcs); get_something (sw_w, XmNverticalScrollBar, (XtArgVal)&sb_w); get_something (sb_w, XmNminimum, (XtArgVal)&min); get_something (sb_w, XmNmaximum, (XtArgVal)&max); get_something (sb_w, XmNsliderSize, (XtArgVal)&slidersize); sbcs.value = value = (min+max-slidersize)/2; set_something (sb_w, XmNvalue, (XtArgVal)value); XtCallCallbacks (sb_w, XmNvalueChangedCallback, &sbcs); } /* set the XmNcolumns resource of the given Text or TextField widget to the * full length of the current string. */ void textColumns (w) Widget w; { Arg args[10]; char *bp; int n; if (XmIsText(w)) bp = XmTextGetString (w); else if (XmIsTextField(w)) bp = XmTextFieldGetString (w); else return; n = 0; XtSetArg (args[n], XmNcolumns, strlen(bp)); n++; XtSetValues (w, args, n); XtFree (bp); } /* check the given XmText or XmTextField value: * if empty, fill with "x/y", or just "x" if !y. * then set size to accommodate if setcols. * while we're at it, fix the text cursor. */ void defaultTextFN (w, setcols, x, y) Widget w; int setcols; char *x, *y; { char *tp = XmTextGetString (w); if (tp[0] == '\0') { char buf[1024]; if (y) sprintf (buf, "%s/%s", x, y); else strcpy (buf, x); XmTextSetString (w, buf); } XtFree (tp); if (setcols) textColumns (w); fixTextCursor (w); } /* turn cursor on/off to follow focus */ static void textFixCursorCB(w, client, call) Widget w; XtPointer client; XtPointer call; { XmAnyCallbackStruct *ap = (XmAnyCallbackStruct *)call; Arg a; XtSetArg (a, XmNcursorPositionVisible, ap->reason == XmCR_FOCUS); XtSetValues (w, &a, 1); } /* call just after creation to work around ugly grey cursor in idle Text or * TextField */ void fixTextCursor (w) Widget w; { Arg a; XtSetArg (a, XmNcursorPositionVisible, False); XtSetValues (w, &a, 1); XtAddCallback (w, XmNfocusCallback, textFixCursorCB, 0); XtAddCallback (w, XmNlosingFocusCallback, textFixCursorCB, 0); } /* convert str to all lowercase IN PLACE */ char * strtolower (char *str) { char *s = str; /* actually faster to /not/ call isupper() first */ do *s = tolower (*s); while (*s++); return (str); } /* check whether op is within its valid date range at np. * if not make a note and return -1 else return 0. */ int dateOK (Now *np, Obj *op) { char dbuf[32]; if (dateRangeOK (np, op) == 0) return (0); fs_date (dbuf, pref_get(PREF_DATE_FORMAT), mjd); xe_msg (0, "Elements for %s are not valid on %s", op->o_name, dbuf); return (-1); } /* set up look_like_button[] and look_like_label[] */ void setButtonInfo() { Pixel topshadcol, botshadcol, bgcol; Pixmap topshadpm, botshadpm; Widget sample; Arg args[20]; int n; n = 0; sample = XmCreatePushButton (toplevel_w, "TEST", args, n); n = 0; XtSetArg (args[n], XmNtopShadowColor, &topshadcol); n++; XtSetArg (args[n], XmNbottomShadowColor, &botshadcol); n++; XtSetArg (args[n], XmNtopShadowPixmap, &topshadpm); n++; XtSetArg (args[n], XmNbottomShadowPixmap, &botshadpm); n++; XtSetArg (args[n], XmNbackground, &bgcol); n++; XtGetValues (sample, args, n); look_like_button[0].value = topshadcol; look_like_button[1].value = botshadcol; look_like_button[2].value = topshadpm; look_like_button[3].value = botshadpm; look_like_label[0].value = bgcol; look_like_label[1].value = bgcol; look_like_label[2].value = XmUNSPECIFIED_PIXMAP; look_like_label[3].value = XmUNSPECIFIED_PIXMAP; XtDestroyWidget (sample); } /* manipulate the given XmButton resources so it indeed looks like a button * or like a label. */ void buttonAsButton (w, whether) Widget w; int whether; { if (!look_like_inited) { setButtonInfo(); look_like_inited = 1; } if (whether) XtSetValues (w, look_like_button, XtNumber(look_like_button)); else XtSetValues (w, look_like_label, XtNumber(look_like_label)); } /* pop up a dialog to allow aborting a lengthy operation. * then check occasionally using stopd_check() */ void stopd_up() { /* create if first time */ if (!stopd_w) { Widget rc_w, w; Arg args[20]; int n; /* create shell */ n = 0; XtSetArg(args[n], XmNcolormap, xe_cm); n++; XtSetArg(args[n], XmNtitle, "xephem Stop"); n++; XtSetArg(args[n], XmNiconName, "Stop"); n++; XtSetArg(args[n], XmNdeleteResponse, XmDO_NOTHING); n++; stopd_w = XtCreatePopupShell("NetStop", topLevelShellWidgetClass, toplevel_w, args, n); setup_icon (stopd_w); set_something (stopd_w, XmNcolormap, (XtArgVal)xe_cm); XtAddCallback (stopd_w, XmNpopupCallback, prompt_map_cb, NULL); /* rc for controls */ n = 0; XtSetArg(args[n], XmNisAligned, True); n++; XtSetArg(args[n], XmNentryAlignment, XmALIGNMENT_CENTER); n++; XtSetArg(args[n], XmNspacing, 10); n++; XtSetArg(args[n], XmNmarginHeight, 10); n++; XtSetArg(args[n], XmNmarginWidth, 10); n++; rc_w = XmCreateRowColumn (stopd_w, "SRC", args, n); XtManageChild (rc_w); /* label and stop button */ n = 0; w = XmCreateLabel (rc_w, "SL", args, n); set_xmstring (w, XmNlabelString, " Press Stop to cancel... "); XtManageChild (w); n = 0; w = XmCreatePushButton (rc_w, "Stop", args, n); XtAddCallback (w, XmNactivateCallback, stopd_cb, NULL); XtManageChild (w); } /* bring it up for sure */ XtPopdown (stopd_w); /* so Popup causes position and raise */ XtPopup (stopd_w, XtGrabNone); set_something (stopd_w, XmNiconic, (XtArgVal)False); XFlush (XtDisplay(stopd_w)); XmUpdateDisplay (stopd_w); XSync (XtDisplay(stopd_w), 0); /* init button not pressed */ stopd_stopped = 0; } /* bring down the user-stop dialog */ void stopd_down() { if (stopd_w) XtPopdown (stopd_w); stopd_stopped = 0; } /* after calling stopd_up(), poll this to check whether the user wants to stop. * return -1 to stop, else 0. */ int stopd_check() { /* check for user button presses */ XCheck (xe_app); if (stopd_stopped) { stopd_stopped = 0; return (-1); } return (0); } /* called when the user presses the Stop button */ /* ARGSUSED */ static void stopd_cb (w, client, call) Widget w; XtPointer client; XtPointer call; { xe_msg (0, "User stop"); stopd_stopped = 1; } /* compare two pointers to strings, qsort style */ static int strc_qs (const void *v1, const void *v2) { return (strcmp (*(char **)v1, *(char **)v2)); } /* called when a pulldown built by createFSM, w, is coming up. * client is an FSBInfo. */ static void FSMfillcb (Widget w, XtPointer client, XtPointer call) { FSMInfo *fsm = (FSMInfo *) client; WidgetList chil; Cardinal nchil; Widget pb_w; char **files; int nfiles; char dirs[2][1024]; int i, j, k; /* init dirs and file list, private first */ sprintf (dirs[0], "%s", getPrivateDir()); sprintf (dirs[1], "%s/%s", getShareDir(), fsm->sharedir); files = NULL; nfiles = 0; /* scan each dir for each suffix, use only first one seen */ for (i = 0; i < 2; i++) { DIR *dirp = opendir (dirs[i]); struct dirent *dp; if (!dirp) continue; while ((dp = readdir(dirp)) != NULL) { for (j = 0; j < fsm->nsuffixes; j++) { int sl = strlen(fsm->suffixes[j]); int fl = strlen(dp->d_name); int dl = fl - sl; if (dl > 0 && !strcmp (dp->d_name+dl, fsm->suffixes[j])) { for (k = 0; k < nfiles; k++) if (strcmp (dp->d_name, files[k]) == 0) break; /* already seen */ if (k == nfiles) { files = (char **) XtRealloc ((char *)files, (nfiles+1)*sizeof(char*)); files[nfiles++] = XtNewString (dp->d_name); } } } } closedir (dirp); } /* sort */ qsort (files, nfiles, sizeof(char *), strc_qs); /* put each file into pulldown, add if more now */ get_something (w, XmNnumChildren, (XtArgVal)&nchil); for (i = 0; i < nfiles; i++) { if (i < nchil) { /* get fresh pointer in case it moves when grown */ get_something (w, XmNchildren, (XtArgVal)&chil); pb_w = chil[i]; } else { /* add new */ pb_w = XmCreatePushButton (w, "FSMPB", NULL, 0); XtAddCallback (pb_w, XmNactivateCallback, fsm->cb, NULL); nchil++; } set_xmstring (pb_w, XmNlabelString, files[i]); XtFree (files[i]); XtManageChild (pb_w); } XtFree ((char *)files); /* multi-column if really long list */ if (nfiles >= 3) set_something (w,XmNnumColumns,(XtArgVal)((int)(sqrt(nfiles/3.0)))); else set_something (w, XmNnumColumns, (XtArgVal)1); /* turn off any extra PBs */ get_something (w, XmNchildren, (XtArgVal)&chil); for (; i < nchil; i++) XtUnmanageChild (chil[i]); } /* create a pulldown that fills itself with names of all files in Shared/sdir * and Private with any of the given suffixes. If parent p is a MenuBar we * build a pulldown triggered by a cascade button and return the cascade button, * else we build and return an option menu. cb will be called when the user * selects an item in the menu, it should get file name from the labelString * resource of the w. * N.B. we assume memory for sdir and each entry in suffix[] are persistent. * N.B. we malloc one new FSMInfo for each pulldown. * N.B. each suffix must include leading '.' */ Widget createFSM (Widget p, char **suffix, int nsuffix, char *sdir, XtCallbackProc cb) { FSMInfo *fsm; Widget pd_w, mgr_w; unsigned char rctype; Arg args[20]; int n; /* gather the info */ fsm = (FSMInfo *) XtMalloc (sizeof(FSMInfo)); fsm->suffixes = (char **) XtMalloc (nsuffix * sizeof(char *)); fsm->nsuffixes = nsuffix; memcpy (fsm->suffixes, suffix, nsuffix * sizeof(char *)); fsm->sharedir = sdir; fsm->cb = cb; /* create the pulldown, allow for multicolumn, connect fsm cb */ n = 0; pd_w = XmCreatePulldownMenu (p, "FSM", args, n); set_something (pd_w, XmNpacking, (XtArgVal)XmPACK_COLUMN); XtAddCallback (pd_w, XmNmapCallback, FSMfillcb, (XtPointer)fsm); /* create the pulldown's manager */ n = 0; XtSetArg (args[n], XmNsubMenuId, pd_w); n++; if (XmIsRowColumn(p) && (get_something (p, XmNrowColumnType, (XtArgVal)&rctype), rctype == XmMENU_BAR)) { mgr_w = XmCreateCascadeButton (p, "FSBCB", args, n); set_xmstring (mgr_w, XmNlabelString, "Files"); } else { /* put one entry for something to show in putton */ Widget pb_w = XmCreatePushButton (pd_w, "XX", NULL, 0); XtAddCallback (pb_w, XmNactivateCallback, cb, NULL); XtManageChild (pb_w); set_xmstring (pb_w, XmNlabelString, "Make selection"); mgr_w = XmCreateOptionMenu (p, "FSBOM", args, n); } XtManageChild (mgr_w); return (mgr_w); } /* look up xcp->pixel in xe_cm and fill in xcp->red/green/blue. * keep a cache of known values to reduce server round trips. * if xcp == NULL reset the cache collection. */ void pixCache (XColor *xcp) { static XColor *xc; static int nxc; int t, b; /* just reset if no xcp */ if (!xcp) { if (xc) { free (xc); xc = NULL; } nxc = 0; return; } /* binary search */ t = nxc - 1; b = 0; while (b <= t) { int m = (t+b)/2; XColor *mxcp = &xc[m]; if (xcp->pixel == mxcp->pixel) { *xcp = *mxcp; return; } if (xcp->pixel < mxcp->pixel) t = m-1; else b = m+1; } /* not found, get the r/g/b */ XQueryColor (XtDisplay(toplevel_w), xe_cm, xcp); /* add to cache in ascending order */ xc = xc ? realloc(xc, (nxc+1)*sizeof(XColor)) : malloc(sizeof(XColor)); for (t = nxc-1; t >= 0 && xc[t].pixel > xcp->pixel; --t) xc[t+1] = xc[t]; xc[t+1] = *xcp; nxc++; } /* For RCS Only -- Do Not Edit */ static char *rcsid[2] = {(char *)rcsid, "@(#) $RCSfile: xmisc.c,v $ $Date: 2006/04/10 09:00:06 $ $Revision: 1.57 $ $Name: $"};