1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)            | */
3 /* |                                                                   | */
4 /* | Permission to use, copy, modify, and to distribute this software  | */
5 /* | and its documentation for any purpose is hereby granted without   | */
6 /* | fee, provided that the above copyright notice appear in all       | */
7 /* | copies and that both that copyright notice and this permission    | */
8 /* | notice appear in supporting documentation.  There is no           | */
9 /* | representations about the suitability of this software for        | */
10 /* | any purpose.  this software is provided "as is" without express   | */
11 /* | or implied warranty.                                              | */
12 /* |                                                                   | */
13 /* +-------------------------------------------------------------------+ */
14 
15 /* $Id: palette.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
16 
17 #ifdef __VMS
18 #define XtDisplay XTDISPLAY
19 #define XtScreen XTSCREEN
20 #endif
21 
22 #include <X11/Intrinsic.h>
23 #include <X11/StringDefs.h>
24 #include <X11/Shell.h>
25 #include <X11/Xatom.h>
26 #include <stdio.h>
27 #include "palette.h"
28 #include "hash.h"
29 #include "messages.h"
30 #include "misc.h"
31 #include "image.h"
32 #include "xpaint.h"
33 
34 typedef struct list_s {
35     Display *dpy;
36     Colormap cmap;
37     Palette *map;
38     struct list_s *next;
39 } PaletteList;
40 
41 typedef struct Col_s {
42     XColor color;
43     Boolean used;
44     Boolean invalid;
45 } Col;
46 
47 #define HASH_SIZE 128
48 #define HASH(c)   	  ((int)((c).red + (c).green + (c).blue) % HASH_SIZE)
49 #define HASH_PIXEL(c)     ((c) % HASH_SIZE)
50 
51 static PaletteList *cmapList = NULL;
52 
53 static int
readCMP(void * cca,void * ccb)54 readCMP(void *cca, void *ccb)
55 {
56     XColor *ca = &((Col *) cca)->color, *cb = &((Col *) ccb)->color;
57 
58     if (ca->red != cb->red)
59 	return ca->red < cb->red ? -1 : 1;
60     if (ca->green != cb->green)
61 	return ca->green < cb->green ? -1 : 1;
62     if (ca->blue != cb->blue)
63 	return ca->blue < cb->blue ? -1 : 1;
64     return 0;
65 }
66 
67 static int
cmpPixel(void * cca,void * ccb)68 cmpPixel(void *cca, void *ccb)
69 {
70     if (((Col *) cca)->color.pixel == ((Col *) ccb)->color.pixel)
71 	return 0;
72     return (((Col *) cca)->color.pixel < ((Col *) ccb)->color.pixel) ? -1 : 1;
73 }
74 
75 static void
freeFunc(void * junk)76 freeFunc(void *junk)
77 {
78     /*
79     **  Nop free func
80      */
81 }
82 
83 static void
entryUnlink(Palette * map,Col * node)84 entryUnlink(Palette * map, Col * node)
85 {
86     node->used = True;
87     node->invalid = False;
88     map->nfree--;
89 }
90 
91 
92 #define STEP	256
93 
94 static Palette *
paletteNew(Widget w,Boolean useDefault)95 paletteNew(Widget w, Boolean useDefault)
96 {
97     int i, v;
98     Display *dpy = XtDisplay(w);
99     Screen *screen = XtScreen(w);
100     Colormap rcmap = DefaultColormapOfScreen(screen);
101     Palette *map = XtNew(Palette);
102     Col *ctable;
103     Visual *visual = NULL;
104     PaletteList *new;
105     int end, depth = -1;
106     Boolean flg = False;
107 
108     PopdownMenusGlobal();
109 
110     XtVaGetValues(w, XtNvisual, &visual, XtNdepth, &depth, NULL);
111     if (visual == NULL || useDefault)
112 	visual = DefaultVisualOfScreen(screen);
113     if (depth <= 0 || useDefault)
114 	depth = DefaultDepthOfScreen(screen);
115 
116     map->htable = NULL;
117     map->ltable = NULL;
118     map->display = dpy;
119     map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
120     map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
121     map->list = NULL;
122     map->last = NULL;
123     map->isMapped = visual->class != TrueColor;
124     map->isGrey = (visual->class == StaticGray || visual->class == GrayScale);
125     map->ncolors = visual->map_entries;
126     map->nfree = 0;
127     map->ctable = NULL;
128     map->visual = visual;
129     map->depth = depth;
130     map->userList = NULL;
131     map->isDefault = False;
132     map->mine = None;
133 
134     switch (visual->class) {
135     case TrueColor:
136 	map->rShift = 0;
137 	map->gShift = 0;
138 	map->bShift = 0;
139 	map->rRange = 1;
140 	map->gRange = 1;
141 	map->bRange = 1;
142 	for (v = visual->red_mask; (v & 1) == 0; v >>= 1)
143 	    map->rShift++;
144 	for (; (v & 1) == 1; v >>= 1)
145 	    map->rRange <<= 1;
146 	for (v = visual->green_mask; (v & 1) == 0; v >>= 1)
147 	    map->gShift++;
148 	for (; (v & 1) == 1; v >>= 1)
149 	    map->gRange <<= 1;
150 	for (v = visual->blue_mask; (v & 1) == 0; v >>= 1)
151 	    map->bShift++;
152 	for (; (v & 1) == 1; v >>= 1)
153 	    map->bRange <<= 1;
154     case StaticGray:
155     case StaticColor:
156 	map->readonly_ = True;
157 	map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
158 				    visual, AllocNone);
159 	map->isDefault = False;
160 	goto addlist;
161     default:
162 	map->readonly_ = False;
163 	if (useDefault) {
164 	    map->cmap = rcmap;
165 	    map->isDefault = True;
166 	} else {
167 	    map->cmap = XCreateColormap(dpy, RootWindowOfScreen(screen),
168 					visual, AllocAll);
169 	}
170 	break;
171     }
172 
173     ctable = (Col *) XtCalloc(sizeof(Col), visual->map_entries);
174     map->ctable = ctable;
175     end = CellsOfScreen(screen);
176     for (i = 0; i < visual->map_entries; i += STEP) {
177 	XColor xcol[STEP];
178 	int cnt = visual->map_entries - i;
179 	int j, d;
180 
181 	if (cnt > STEP)
182 	    cnt = STEP;
183 
184 	for (j = 0; j < cnt; j++) {
185 	    Col *c = &ctable[i + j];
186 
187 	    c->color.pixel = i + j;
188 	    c->color.flags = DoRed | DoGreen | DoBlue;
189 	    xcol[j].pixel = i + j;
190 	    xcol[j].flags = DoRed | DoGreen | DoBlue;
191 
192 	    c->used = False;
193 	    c->invalid = False;
194 	}
195 
196 	if (i >= end)
197 	    d = 0;
198 	else
199 	    d = MIN(end - i, cnt);
200 
201 	if (i < end)
202 	    XQueryColors(dpy, rcmap, xcol, d);
203 	if (!flg) {
204 	    for (j = d; j < cnt; j++) {
205 		xcol[j].flags = DoRed | DoGreen | DoBlue;
206 		xcol[j].red = xcol[j].green = xcol[j].blue = 0xffff;
207 	    }
208 	    if (d == 0)
209 		flg = True;
210 	}
211 	if (!map->isDefault)
212 	    XStoreColors(dpy, map->cmap, xcol, cnt);
213 
214 	for (j = 0; j < cnt; j++) {
215 	    Col *c = &ctable[i + j];
216 
217 	    c->color.red = xcol[j].red & 0xff00;
218 	    c->color.green = xcol[j].green & 0xff00;
219 	    c->color.blue = xcol[j].blue & 0xff00;
220 	    HashAdd(map->htable, HASH(c->color), c);
221 	    HashAdd(map->ltable, HASH_PIXEL(c->color.pixel), c);
222 	}
223     }
224 
225     map->nfree = visual->map_entries;
226 
227     if (!map->isDefault) {
228 	Boolean got = False;
229 
230 	got = !map->isMapped;
231 	for (i = 0; i < visual->map_entries; i++) {
232 	    if (ctable[i].color.pixel == BlackPixelOfScreen(screen)) {
233 		entryUnlink(map, &ctable[i]);
234 	    } else if (ctable[i].color.pixel == WhitePixelOfScreen(screen)) {
235 		entryUnlink(map, &ctable[i]);
236 	    } else if (!got) {
237 		map->mine = i;
238 		entryUnlink(map, &ctable[i]);
239 		got = True;
240 	    }
241 	}
242     }
243   addlist:
244     new = (PaletteList *) XtMalloc(sizeof(PaletteList));
245     new->dpy = XtDisplay(w);
246     new->cmap = map->cmap;
247     new->next = cmapList;
248     new->map = map;
249     cmapList = new;
250 
251     return map;
252 }
253 
254 Palette *
PaletteCreate(Widget w)255 PaletteCreate(Widget w)
256 {
257     return paletteNew(GetShell(w), False);
258 }
259 
260 Palette *
PaletteGetDefault(Widget w)261 PaletteGetDefault(Widget w)
262 {
263     static Palette *defMap = NULL;
264 
265     if (defMap == NULL)
266 	defMap = paletteNew(GetShell(w), True);
267 
268     return defMap;
269 }
270 
271 static Palette *
paletteGetBW(void)272 paletteGetBW(void)
273 {
274     static Palette *map = NULL;
275     Col *ctable;
276 
277     if (map != NULL)
278 	return map;
279 
280     map = XtNew(Palette);
281 
282     map->display = NULL;
283     map->htable = HashCreate(readCMP, freeFunc, HASH_SIZE);
284     map->ltable = HashCreate(cmpPixel, freeFunc, HASH_SIZE);
285     map->list = NULL;
286     map->last = NULL;
287     map->isMapped = True;
288     map->isGrey = True;
289     map->ncolors = 2;
290     map->nfree = 0;
291     map->ctable = NULL;
292     map->visual = None;		/* X */
293     map->depth = 1;
294     map->userList = NULL;
295     map->isDefault = False;
296     map->mine = None;
297     map->cmap = None;
298 
299     ctable = (Col *) XtCalloc(sizeof(Col), map->ncolors);
300     map->ctable = ctable;
301 
302     ctable[0].color.pixel = 0;
303     ctable[0].color.flags = DoRed | DoGreen | DoBlue;
304     ctable[0].color.red = 0xffff;
305     ctable[0].color.green = 0xffff;
306     ctable[0].color.blue = 0xffff;
307     ctable[0].used = True;
308     ctable[0].invalid = False;
309 
310     ctable[1].color.pixel = 1;
311     ctable[1].color.flags = DoRed | DoGreen | DoBlue;
312     ctable[1].color.red = 0x0000;
313     ctable[1].color.green = 0x0000;
314     ctable[1].color.blue = 0x0000;
315     ctable[1].used = True;
316     ctable[1].invalid = False;
317 
318     HashAdd(map->htable, HASH(ctable[0].color), &ctable[0]);
319     HashAdd(map->htable, HASH(ctable[1].color), &ctable[1]);
320     HashAdd(map->ltable, HASH_PIXEL(ctable[0].color.pixel), &ctable[0]);
321     HashAdd(map->ltable, HASH_PIXEL(ctable[1].color.pixel), &ctable[1]);
322 
323     return map;
324 }
325 
326 static void
addColor(Palette * map,XColor * color)327 addColor(Palette * map, XColor * color)
328 {
329     Col *node = NULL, *n, *cptr;
330     int i;
331 
332 
333     if (map->readonly_) {
334 	/*
335 	**  The temporary color is needed since alloc
336 	**  will change the pixel values.
337 	 */
338 	XColor newc;
339 	newc = *color;
340 	node = XtNew(Col);
341 
342 	newc.flags = DoRed | DoGreen | DoBlue;
343 	XAllocColor(map->display, map->cmap, &newc);
344 	node->color.pixel = color->pixel = newc.pixel;
345     } else {
346 	if (map->nfree == 0) {
347 	    unsigned int d, curDif = ~0;
348 	    int rd, gd, bd;
349 	    /*
350 	    **  All free colors used up -- use closest match.
351 	     */
352 	    for (i = 0, cptr = (Col *) map->ctable; i < map->ncolors; i++, cptr++) {
353 		rd = (cptr->color.red >> 8) - (color->red >> 8);
354 		gd = (cptr->color.green >> 8) - (color->green >> 8);
355 		bd = (cptr->color.blue >> 8) - (color->blue >> 8);
356 		d = rd * rd + gd * gd + bd * bd;
357 		if (d < curDif) {
358 		    node = cptr;
359 		    curDif = d;
360 		}
361 	    }
362 	    color->pixel = node->color.pixel;
363 	    return;
364 	}
365 	/* Find first free entry -- we know it's there. */
366 	for (i = 0, node = (Col *) map->ctable;
367 	     (i < map->ncolors) && node->used; i++, node++);
368 	entryUnlink(map, node);	/* mark as used */
369 
370 	color->pixel = node->color.pixel;
371 	n = HashFind(map->ltable, HASH_PIXEL(node->color.pixel), node);
372 	HashRemove(map->ltable, HASH_PIXEL(node->color.pixel), n);
373 	HashRemove(map->htable, HASH(*color), node);
374     }
375 
376     node->used = True;
377     node->invalid = False;
378     node->color.red = color->red & 0xff00;
379     node->color.green = color->green & 0xff00;
380     node->color.blue = color->blue & 0xff00;
381     node->color.flags = DoRed | DoGreen | DoBlue;
382     if (!map->readonly_)
383 	XStoreColor(map->display, map->cmap, &node->color);
384     HashAdd(map->htable, HASH(*color), node);
385     HashAdd(map->ltable, HASH_PIXEL(color->pixel), node);
386 }
387 
388 /*
389  * Try to allocate the specified XColor in the colormap.
390  * If the color is already in the palette, use that instead.
391  * Return the corresponding Pixel value in 'list'.
392  */
393 static Col *
allocN(Palette * map,XColor * color,Pixel * list)394 allocN(Palette * map, XColor * color, Pixel * list)
395 {
396     Col c, *node;
397 
398     c.color.red = color->red & 0xff00;
399     c.color.green = color->green & 0xff00;
400     c.color.blue = color->blue & 0xff00;
401 
402     if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
403 	addColor(map, color);
404 	*list = color->pixel;
405     } else {
406 	/*
407 	**  It must have been allocated in the previous
408 	**   pass, or by the above.
409 	 */
410 	if (!node->used)
411 	    entryUnlink(map, node);
412 	*list = node->color.pixel;
413     }
414 
415     return node;
416 }
417 
418 int
PaletteAllocN(Palette * map,XColor * color,int ncolor,Pixel * list)419 PaletteAllocN(Palette * map, XColor * color, int ncolor, Pixel * list)
420 {
421     Boolean *flg = XtCalloc(sizeof(Boolean), ncolor);
422     Col c, *node;
423     int i;
424     Boolean newMine = False;
425 
426     if (!map->isMapped) {
427 	for (i = 0; i < ncolor; i++) {
428 	    unsigned int r, g, b;
429 
430 	    r = (color[i].red * map->rRange) >> 16;
431 	    g = (color[i].green * map->gRange) >> 16;
432 	    b = (color[i].blue * map->bRange) >> 16;
433 	    list[i] = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
434 	}
435 	return 0;
436     }
437     for (i = 0; i < ncolor; i++) {
438 	Col c;
439 
440 	c.color.red = color[i].red & 0xff00;
441 	c.color.green = color[i].green & 0xff00;
442 	c.color.blue = color[i].blue & 0xff00;
443 
444 	if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
445 	    flg[i] = True;
446 	    /*
447 	    **  Match found, if the entry hasn't been "alloced"
448 	    **   yet, mark it so, and remove it from the "free" list.
449 	     */
450 	    if (!node->used)
451 		entryUnlink(map, node);
452 	    list[i] = node->color.pixel;
453 	    if (list[i] == map->mine)
454 		newMine = True;
455 	} else {
456 	    flg[i] = False;
457 	}
458     }
459 
460     for (i = 0; i < ncolor; i++) {
461 	if (flg[i])
462 	    continue;
463 
464 	c.color.red = color[i].red & 0xff00;
465 	c.color.green = color[i].green & 0xff00;
466 	c.color.blue = color[i].blue & 0xff00;
467 
468 	if ((node = HashFind(map->htable, HASH(c.color), &c)) == NULL) {
469 	    addColor(map, &color[i]);
470 	    list[i] = color[i].pixel;
471 	} else {
472 	    /*
473 	    **  It must have been allocated in the previous
474 	    **   pass, or by the above.
475 	     */
476 	    list[i] = node->color.pixel;
477 	}
478 	if (list[i] == map->mine)
479 	    newMine = True;
480     }
481 
482     XtFree((XtPointer) flg);
483 
484     if (newMine && map->ctable != NULL) {
485 	Col *cptr = (Col *) map->ctable;
486 
487 	for (i = 0; i < map->ncolors; i++, cptr++) {
488 	    if (cptr->used)
489 		continue;
490 	    map->mine = cptr->color.pixel;
491 	    break;
492 	}
493     }
494     return 0;
495 }
496 
497 /*
498  * Return the Pixel corresponding to (or closest to) the RGB
499  * values specified in 'color'.
500  * If no match is found in the existing palette, try to allocate
501  * a new color.
502  */
503 Pixel
PaletteAlloc(Palette * map,XColor * color)504 PaletteAlloc(Palette * map, XColor * color)
505 {
506     if (!map->isMapped) {
507 	unsigned int r, g, b;
508 
509 	r = (color->red * map->rRange) >> 16;
510 	g = (color->green * map->gRange) >> 16;
511 	b = (color->blue * map->bRange) >> 16;
512 	return (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
513     }
514     if (map->last != NULL) {
515 	XColor *lc = (XColor *) map->last;
516 
517 	if (lc->red == color->red &&
518 	    lc->green == color->green &&
519 	    lc->blue == color->blue)
520 	    return color->pixel = lc->pixel;
521     }
522     map->last = allocN(map, color, &color->pixel);
523 
524     return color->pixel;
525 }
526 
527 /*
528 **  Given a Pixel value on the specified map return the
529 **   RGB value.
530 **  On non-TrueColor displays, the returned value is actually a pointer
531 **   into map->ctable[].
532 **
533 **   Special case "TrueColor" since it is just computed.
534  */
535 XColor *
PaletteLookup(Palette * map,Pixel pix)536 PaletteLookup(Palette * map, Pixel pix)
537 {
538     if (map->isMapped) {
539 	Col col;
540 	Col *c;
541 	col.color.pixel = pix;
542 
543 	if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL) {
544 	    fprintf(stderr,"%s", msgText[SHOULDNT_HAPPEN]);
545 	    return NULL;
546 	}
547 	if (c->invalid) {
548 	    HashRemove(map->htable, HASH(c->color), c);
549 	    XQueryColor(map->display, map->cmap, &c->color);
550 	    c->color.red &= 0xff00;
551 	    c->color.green &= 0xff00;
552 	    c->color.blue &= 0xff00;
553 	    HashAdd(map->htable, HASH(c->color), c);
554 	    c->invalid = False;
555 	}
556 	return &c->color;
557     } else {
558 	static XColor xc;
559 
560 	xc.red = (pix >> map->rShift) & (map->rRange - 1);
561 	xc.green = (pix >> map->gShift) & (map->gRange - 1);
562 	xc.blue = (pix >> map->bShift) & (map->bRange - 1);
563 
564 	xc.red *= 65536 / map->rRange;
565 	xc.green *= 65536 / map->gRange;
566 	xc.blue *= 65536 / map->bRange;
567 
568 	return &xc;
569     }
570 }
571 
572 Boolean
PaletteLookupColor(Palette * map,XColor * col,Pixel * pxl)573 PaletteLookupColor(Palette * map, XColor * col, Pixel * pxl)
574 {
575     Col c, *node;
576 
577     if (!map->isMapped) {
578 	unsigned int r, g, b;
579 
580 	r = (col->red * map->rRange) >> 16;
581 	g = (col->green * map->gRange) >> 16;
582 	b = (col->blue * map->bRange) >> 16;
583 	*pxl = (r << map->rShift) | (g << map->gShift) | (b << map->bShift);
584 	return True;
585     }
586     c.color.red = col->red & 0xff00;
587     c.color.green = col->green & 0xff00;
588     c.color.blue = col->blue & 0xff00;
589 
590     if ((node = HashFind(map->htable, HASH(c.color), &c)) != NULL) {
591 	*pxl = node->color.pixel;
592 	return True;
593     }
594     return False;
595 }
596 
597 Pixel
PaletteGetUnused(Palette * map)598 PaletteGetUnused(Palette * map)
599 {
600     return map->mine;
601 }
602 
603 /*
604 **  Change the RGB value of a pixel
605  */
606 void
PaletteSetInvalid(Palette * map,Pixel pix)607 PaletteSetInvalid(Palette * map, Pixel pix)
608 {
609     Col col, *c;
610 
611     if (!map->isMapped || map->readonly_)
612 	return;
613 
614     col.color.pixel = pix;
615 
616     if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pix), &col)) == NULL)
617 	return;
618 
619     c->invalid = True;
620 }
621 
622 /*
623 **  This will fail if on a read only colormap
624  */
625 Boolean
PaletteSetPixel(Palette * map,Pixel pixel,XColor * xcol)626 PaletteSetPixel(Palette * map, Pixel pixel, XColor * xcol)
627 {
628     Col col, *c;
629 
630     if (map->readonly_)
631 	return False;
632 
633     if (map->isDefault)
634 	return False;
635 
636     xcol->pixel = pixel;
637     xcol->flags = DoRed | DoGreen | DoBlue;
638     XStoreColor(map->display, map->cmap, xcol);
639 
640     col.color.pixel = pixel;
641 
642     if ((c = (Col *) HashFind(map->ltable, HASH_PIXEL(pixel), &col)) != NULL)
643 	c->invalid = True;
644 
645     return True;
646 }
647 
648 Palette *
PaletteFindDpy(Display * dpy,Colormap cmap)649 PaletteFindDpy(Display * dpy, Colormap cmap)
650 {
651     PaletteList *cur;
652 
653     if (cmap == -1)
654 	return paletteGetBW();
655 
656     for (cur = cmapList; cur != NULL; cur = cur->next)
657 	if (cur->cmap == cmap && cur->dpy == dpy)
658 	    return cur->map;
659 
660     return NULL;
661 }
662 
663 Palette *
PaletteFind(Widget w,Colormap cmap)664 PaletteFind(Widget w, Colormap cmap)
665 {
666     return PaletteFindDpy(XtDisplay(w), cmap);
667 }
668 
669 static void
paletteAddUserDestroy(Widget w,Palette * map,XtPointer junk)670 paletteAddUserDestroy(Widget w, Palette * map, XtPointer junk)
671 {
672     paletteUserList *cur = map->userList;
673     paletteUserList **prev = &map->userList;
674 
675     while (cur != NULL && cur->widget != w) {
676 	prev = &cur->next;
677 	cur = cur->next;
678     }
679 
680     if (cur == NULL)
681 	return;
682 
683     *prev = cur->next;
684     XtFree((XtPointer) cur);
685 }
686 
687 void
PaletteDelete(Palette * map)688 PaletteDelete(Palette * map)
689 {
690     PaletteList *cur, **prev;
691     paletteUserList *ul, *nx;
692 
693     HashDestroy(map->htable);
694     HashDestroy(map->ltable);
695     if (map->ctable != NULL)
696 	XtFree((XtPointer) map->ctable);
697 
698     for (cur = cmapList, prev = &cmapList; cur != NULL && cur->map != map;
699 	 prev = &cur->next, cur = cur->next);
700 
701     if (cur != NULL)
702 	*prev = cur->next;
703 
704     ul = map->userList;
705     while (ul != NULL) {
706 	nx = ul->next;
707 	XtRemoveCallback(ul->widget, XtNdestroyCallback,
708 			 (XtCallbackProc) paletteAddUserDestroy,
709 			 (XtPointer) map);
710 	XtFree((XtPointer) ul);
711 	ul = nx;
712     }
713 
714     XtFree((XtPointer) map);
715     XtFree((XtPointer) cur);
716 }
717 
718 void
PaletteAddUser(Palette * map,Widget w)719 PaletteAddUser(Palette * map, Widget w)
720 {
721     paletteUserList *n = XtNew(paletteUserList);
722 
723     n->widget = w;
724     n->next = map->userList;
725     map->userList = n;
726     XtAddCallback(w, XtNdestroyCallback,
727 		  (XtCallbackProc) paletteAddUserDestroy, (XtPointer) map);
728 }
729