1 /*
2  * FIG : Facility for Interactive Generation of figures
3  * Parts Copyright (c) 1994-2007 by Brian V. Smith
4  * Parts Copyright 1990,1992 Richard Hesketh
5  *          Computing Lab. University of Kent at Canterbury, UK
6  *
7  * Pixel Grab color lookup Copyright 1993, David Koblas (koblas@netcom.com)
8  * and Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)
9  * (full copyright and permission notice appears above code)
10  *
11  * Any party obtaining a copy of these files is granted, free of charge, a
12  * full and unrestricted irrevocable, world-wide, paid up, royalty-free,
13  * nonexclusive right and license to deal in this software and documentation
14  * files (the "Software"), including without limitation the rights to use,
15  * copy, modify, merge, publish, distribute, sublicense and/or sell copies of
16  * the Software, and to permit persons who receive copies from any such
17  * party to do so, with the only requirement being that the above copyright
18  * and this permission notice remain intact.
19  *
20  ****************************************************************************
21  *
22  * Parts of this work were extracted from Richard Hesketh's xcoloredit
23  * client. Here is the copyright notice which must remain intact:
24  *
25  * Copyright 1990,1992 Richard Hesketh / rlh2@ukc.ac.uk
26  *                Computing Lab. University of Kent at Canterbury, UK
27  *
28  * Permission to use, copy, modify and distribute this software and its
29  * documentation for any purpose is hereby granted without fee, provided that
30  * the above copyright notice appear in all copies and that both that
31  * copyright notice and this permission notice appear in supporting
32  * documentation, and that the names of Richard Hesketh and The University of
33  * Kent at Canterbury not be used in advertising or publicity pertaining to
34  * distribution of the software without specific, written prior permission.
35  * Richard Hesketh and The University of Kent at Canterbury make no
36  * representations about the suitability of this software for any purpose.
37  * It is provided "as is" without express or implied warranty.
38  *
39  * Richard Hesketh AND THE UNIVERSITY OF KENT AT CANTERBURY DISCLAIMS ALL
40  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
41  * OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Richard Hesketh OR THE
42  * UNIVERSITY OF KENT AT CANTERBURY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
43  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
44  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
45  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
46  * OF THIS SOFTWARE.
47  *
48  * Author:  Richard Hesketh / rlh2@ukc.ac.uk,
49  *                Computing Lab. University of Kent at Canterbury, UK
50  */
51 
52 /* A picapix lookalike under X11 */
53 
54 /*
55  * xcoloredit - a colour palette mixer.  Allows existing colormap entries
56  *		to be edited.
57  */
58 
59 #include "fig.h"
60 #include "figx.h"
61 #include "resources.h"
62 #include "mode.h"
63 #include "object.h"
64 #include "w_drawprim.h"
65 #include "w_indpanel.h"
66 #include "w_color.h"
67 #include "w_msgpanel.h"
68 #include "w_setup.h"
69 #include "w_util.h"
70 
71 #include "f_util.h"
72 
73 /* EXPORTS */
74 
75 Widget	delunusedColors;
76 
77 /* LOCALS */
78 
79 DeclareStaticArgs(30);
80 
81 /* callback routines */
82 static void cancel_color_popup(Widget w, XtPointer closure, XtPointer call_data);
83 static void Thumbed(Widget w, XtPointer closure, XtPointer call_data);
84 static void Scrolled(Widget w, XtPointer closure, XtPointer call_data);
85 static void Update_HSV(Widget w, XtPointer closure, XtPointer call_data);
86 static void switch_edit(Widget w, XtPointer client_data, XtPointer call_data);
87 
88 /* action routines */
89 static void lock_toggle(Widget w, XEvent *event, String *params, Cardinal *num_params);
90 static void _pick_memory(Widget w, XEvent *event, String *params, Cardinal *num_params);
91 static void pick_memory(int which);
92 static void update_triple(void);
93 static void update_scrl_triple(Widget w, XEvent *event, String *params, Cardinal *num_params);
94 static void update_from_triple(Widget w, XEvent *event, String *params, Cardinal *num_params);
95 static void move_scroll(Widget w, XEvent *event, String *params, Cardinal *num_params);
96 static void set_color_ok(Widget w, char *dum, XButtonEvent *ev, Boolean disp);
97 static void _set_std_color(Widget w, choice_info *sel_choice, XButtonEvent *ev);
98 static void set_std_color(int color);
99 static void add_color(Widget w, XtPointer closure, XtPointer call_data);
100 static void del_color(Widget w, XtPointer closure, XtPointer call_data);
101 static void undel_color(Widget w, XtPointer closure, XtPointer call_data);
102 static void lookup_color(Widget w, XtPointer closure, XtPointer call_data);
103 static void del_unused_user_colors(void);
104 
105 /* some local procedures */
106 
107 static int WhichButton(String name);
108 static Boolean color_used(int color, F_compound *list);
109 static void DoGrabPixel(Widget w, Pixel *p, Colormap *cmap);
110 static void doGrab(Widget w, int width, int height, int *x, int *y);
111 static void xyToWindowCmap(Display *dpy, int x, int y, Window base, int *nx, int *ny, Window *window, Colormap *cmap);
112 static void draw_boxed(int which);
113 static void erase_boxed(int which);
114 static void c_user_colors(F_compound *obj);
115 static void set_mixed_name(int i, int col);
116 
117 #define S_RED    1
118 #define S_GREEN  2
119 #define S_BLUE   4
120 #define S_LOCKED 8
121 #define S_HUE    16
122 #define S_SAT    32
123 #define S_VAL    64
124 
125 /* width/height and space between the scrollbars */
126 #define SCROLL_W	23
127 #define SCROLL_H	120
128 #define SCROLL_SP	4
129 
130 /* width/height of the standard color cells */
131 #define STD_COL_W	31
132 #define STD_COL_H	20
133 
134 /* width/height of the user color cells */
135 #define USR_COL_W	30
136 #define USR_COL_H	20
137 /* spacing between cells */
138 #define USR_COL_SP	2
139 
140 /* thickness (height) of the scrollbar (fraction of total) */
141 #define	THUMB_H		0.04
142 
143 #define COLOR(color_el,rgb) ((color_el<0)? \
144 			mixed_color[color_el+2].rgb/256: user_colors[color_el].rgb/256)
145 #define CHANGE_RED(element) \
146 		pass_value = 1.0 - (float)(COLOR(element,red)/255.0); \
147 		Thumbed(redScroll, (XtPointer)S_RED, (XtPointer)(&pass_value))
148 #define CHANGE_GREEN(element) \
149 		pass_value = 1.0 - (float)(COLOR(element,green)/255.0); \
150 		Thumbed(greenScroll, (XtPointer)S_GREEN, (XtPointer)(&pass_value))
151 #define CHANGE_BLUE(element) \
152 		pass_value = 1.0 - (float)(COLOR(element,blue)/255.0); \
153 		Thumbed(blueScroll, (XtPointer)S_BLUE, (XtPointer)(&pass_value))
154 
155 static RGB	rgb_values[2];
156 static HSV	hsv_values;
157 static int	buttons_down = 0;
158 static int	bars_locked = 0;
159 static float	locked_top = 1.0;
160 static float	red_top, green_top, blue_top;
161 static float	pass_value;
162 static int	last_pos;
163 static Boolean	do_change = True;
164 static Boolean	modified[2];
165 static intptr_t	edit_fill;
166 static Pixel	original_background;
167 static XColor	mixed_color[2];
168 static int	mixed_color_indx[2];
169 static int	colors_used[MAX_USR_COLS];
170 
171 static Boolean	moving_hsv = False;
172 
173 static unsigned long	plane_masks;
174 static unsigned long	pixels[MAX_USR_COLS];
175 
176 static Widget	mixingForm;
177 static Widget	mixedForm[2], mixedColor[2], mixedEdit[2];
178 static Widget	tripleValue[2];
179 static Widget	lockedLabel;
180 static Widget	redLocked, greenLocked, blueLocked;
181 static Widget	redScroll, greenScroll, blueScroll, lockedScroll;
182 static Widget	hueScroll, satScroll, valScroll;
183 static Widget	hueLabel, satLabel, valLabel;
184 static Widget	ok_button;
185 static Widget	addColor, delColor, undelColor, lookupColor;
186 static Widget	colorCount;
187 static Widget	userLabel, userForm, userViewport, userBox;
188 static Widget	colorMemory[MAX_USR_COLS];
189 
190 static XtActionsRec actionTable[] = {
191 	{"lock_toggle", lock_toggle},
192 	{"pick_memory", _pick_memory},
193 	{"update_scrl_triple", update_scrl_triple},
194 	{"update_from_triple", update_from_triple},
195 	{"set_std_color", (XtActionProc)_set_std_color},
196 	{"set_color_ok", (XtActionProc)set_color_ok},
197 };
198 
199 static String set_panel_translations =
200 	"<Key>Return: set_color_ok()\n";
201 
202 static String edit_translations =
203 	"<EnterWindow>: highlight(Always)\n\
204 	<LeaveWindow>:	unhighlight()\n\
205 	<Btn1Down>,<Btn1Up>: set() notify()\n";
206 
207 static String redLocked_translations =
208 	"<Btn1Down>: lock_toggle(red)\n";
209 static String greenLocked_translations =
210 	"<Btn1Down>: lock_toggle(green)\n";
211 static String blueLocked_translations =
212 	"<Btn1Down>: lock_toggle(blue)\n";
213 
214 static String std_color_translations =
215 	"<Btn1Up>: set_std_color()\n\
216 	<Btn1Up>(2): set_color_ok()\n";
217 
218 static String user_color_translations =
219 	"<Btn1Down>: pick_memory()\n\
220 	<Btn1Down>(2): set_color_ok()\n";
221 
222 static String triple_translations =
223 	"<Key>Return: update_from_triple()\n";
224 
225 
226 void create_cell (int indx, XColor color);
227 void set_cmap (Window window);
228 void set_slider_sensitivity (void);
229 void set_mixed_color (int which);
230 void pick_contrast (XColor color, Widget widget);
231 int add_color_cell (Boolean use_exist, int indx, int r, int g, int b);
232 void count_one (int color);
233 void move_lock (void);
234 void StoreMix_and_Mem (void);
235 void ThumbHSV (Widget w, float top);
236 
YStoreColor(Colormap colormap,XColor * color)237 void YStoreColor(Colormap colormap, XColor *color)
238 {
239 	switch( tool_vclass ){
240 	    case GrayScale:
241 	    case PseudoColor:
242 		XStoreColor(tool_d,colormap,color);
243 		break;
244 	    case StaticGray:
245 	    case StaticColor:
246 	    case DirectColor:
247 	    case TrueColor:
248 		XAllocColor(tool_d,colormap,color);
249 		break;
250 	    default:
251 		fprintf(stderr,"Unknown Visual Class\n");
252 	}
253 }
254 
YStoreColors(Colormap colormap,XColor * color,int ncolors)255 void YStoreColors(Colormap colormap, XColor *color, int ncolors)
256 {
257 	int		 i;
258 
259 	for( i=0; i<ncolors; i++ )
260 	    YStoreColor(colormap,&color[i]);
261 }
262 
create_color_panel(Widget form,Widget label,Widget cancel,ind_sw_info * isw)263 void create_color_panel(Widget form, Widget label, Widget cancel, ind_sw_info *isw)
264 {
265 	intptr_t	 i;
266 	choice_info	*choice;
267 	XColor		 col;
268 	Pixel		 form_fg;
269 	Widget		 below, beside, stdForm, stdLabel;
270 	Widget		 sb;
271 	char		 str[8];
272 
273 	current_memory = -1;
274 	edit_fill = 0;
275 	modified[0] = modified[1] = False;
276 
277 	/* add a callback to call a cancel function here besides the main one */
278 	XtAddCallback(cancel, XtNcallback, cancel_color_popup, (XtPointer) 0);
279 
280 	pixels[0] = pixels[1] = 0;
281 
282 	if (all_colors_available) {
283 	    /* allocate two colorcells for the mixed fill and pen color widgets */
284 	    if (!alloc_color_cells(pixels,2)) {
285 		file_msg("Can't allocate necessary colorcells, can't popup color panel");
286 		XtDestroyWidget(isw->panel);	/* the panel hasn't been fully created */
287 		isw->panel = NULL;
288 		return;
289 	    }
290 	    mixed_color[0].pixel = pixels[0];
291 	    mixed_color[1].pixel = pixels[1];
292 	}
293 
294 	/* make carriage return anywhere in form do the OK button */
295 	XtOverrideTranslations(form,
296 			       XtParseTranslationTable(set_panel_translations));
297 
298 	/* make the OK button */
299 	FirstArg(XtNlabel, "  Ok  ");
300 	NextArg(XtNfromVert, cancel);
301 	NextArg(XtNborderWidth, INTERNAL_BW);
302 	NextArg(XtNtop, XtChainTop);
303 	NextArg(XtNbottom, XtChainTop);
304 	NextArg(XtNleft, XtChainLeft);
305 	NextArg(XtNright, XtChainLeft);
306 	ok_button = XtCreateManagedWidget("set_color_ok", commandWidgetClass,
307 				       form, Args, ArgCount);
308 	XtAddEventHandler(ok_button, ButtonReleaseMask, False,
309 			  (XtEventHandler) set_color_ok, (XtPointer) NULL);
310 
311 	/* put the fill and pen colors down the left side */
312 
313 	for (i=0; i<2; i++) {
314 		/* wrap the edit fill/color panels in a form widget */
315 		if (i==0) {
316 		    FirstArg(XtNfromVert, ok_button);
317 		    NextArg(XtNvertDistance, 30);
318 		} else {
319 		    FirstArg(XtNfromVert, mixedForm[0]);
320 		    NextArg(XtNvertDistance, 20);
321 		}
322 		NextArg(XtNtop, XtChainTop);
323 		NextArg(XtNbottom, XtChainTop);
324 		NextArg(XtNleft, XtChainLeft);
325 		NextArg(XtNright, XtChainLeft);
326 		mixedForm[i] = XtCreateManagedWidget("mixedForm", formWidgetClass,
327 						form, Args, ArgCount);
328 
329 		FirstArg(XtNlabel, (i==0)? "Edit Pen": "Edit Fill");
330 		NextArg(XtNwidth, 70);
331 		/* make it so that only one of the edit buttons are pressed */
332 		if (i)
333 		    NextArg(XtNradioGroup, mixedEdit[0]);
334 		else
335 		    NextArg(XtNstate, True);	/* start with edit pen */
336 		NextArg(XtNradioData, (XtPointer) (i+1));	/* can't use 0 */
337 		mixedEdit[i] = XtCreateManagedWidget("mixedEdit", toggleWidgetClass,
338 							mixedForm[i], Args, ArgCount);
339 		XtAddCallback(mixedEdit[i], XtNcallback, switch_edit, (XtPointer) 0);
340 
341 		/* set up so that at one is always set */
342 		XtOverrideTranslations(mixedEdit[i],
343 				       XtParseTranslationTable(edit_translations));
344 
345 		FirstArg(XtNbackground, mixed_color[i].pixel);
346 		NextArg(XtNwidth, 70);
347 		NextArg(XtNheight, 70);
348 		NextArg(XtNfromVert, mixedEdit[i]);
349 		NextArg(XtNvertDistance, 2);
350 		NextArg(XtNresize, False);
351 		mixedColor[i] = XtCreateManagedWidget("mixedColor", labelWidgetClass,
352 						mixedForm[i], Args, ArgCount);
353 
354 		strcpy(str,"#000000");
355 		FirstArg(XtNstring, str);
356 		NextArg(XtNinsertPosition, strlen(str));
357 		NextArg(XtNeditType, XawtextEdit);
358 		NextArg(XtNwidth, 70);
359 		NextArg(XtNfromVert, mixedColor[i]);
360 		NextArg(XtNvertDistance, 2);
361 		tripleValue[i] = XtCreateManagedWidget("tripleValue", asciiTextWidgetClass,
362 							mixedForm[i], Args, ArgCount);
363 		/* set value on carriage return */
364 		XtOverrideTranslations(tripleValue[i],
365 				       XtParseTranslationTable(triple_translations));
366 	}
367 	/* make pen and fill hex value insensitive to start */
368 	/* (until user picks memory) */
369 	XtSetSensitive(tripleValue[0], False);
370 	XtSetSensitive(tripleValue[1], False);
371 
372 	/********************************/
373 	/* now the standard color panel */
374 	/********************************/
375 
376 	FirstArg(XtNlabel, "Standard Colors");
377 	NextArg(XtNfromHoriz, mixedForm[0]);
378 	NextArg(XtNtop, XtChainTop);
379 	NextArg(XtNbottom, XtChainTop);
380 	NextArg(XtNleft, XtChainLeft);
381 	NextArg(XtNright, XtChainLeft);
382 	stdLabel = XtCreateManagedWidget("stdLabel", labelWidgetClass,
383 						form, Args, ArgCount);
384 
385 	/* put them in a form just to make a nice border */
386 	FirstArg(XtNfromVert, stdLabel);
387 	NextArg(XtNvertDistance, 0);
388 	NextArg(XtNfromHoriz, mixedForm[0]);
389 	NextArg(XtNborderWidth, 2);
390 	NextArg(XtNtop, XtChainTop);
391 	NextArg(XtNbottom, XtChainTop);
392 	NextArg(XtNleft, XtChainLeft);
393 	NextArg(XtNright, XtChainLeft);
394 	stdForm = XtCreateManagedWidget("stdForm", formWidgetClass,
395 						form, Args, ArgCount);
396 	choice = isw->choices;
397 	/* create the standard color buttons */
398 	for (i = 0; i < isw->numchoices; choice++, i++) {
399 		FirstArg(XtNheight, STD_COL_H);
400 		choice->value = (i >= NUM_STD_COLS ? DEFAULT : i);
401 		/* check for new row of buttons */
402 		if (i % isw->sw_per_row == 0) {
403 		    if (i == 0)
404 			below = NULL;
405 		    else
406 			below = beside;
407 		    beside = NULL;
408 		}
409 		/* standard color menu */
410 		if (i < NUM_STD_COLS && i >= 0) {
411 		    if (all_colors_available) {
412 			col.pixel = x_color(i);
413 			XQueryColor(tool_d, tool_cm, &col);
414 			if ((0.3 * col.red + 0.59 * col.green + 0.11 * col.blue) < 0.5 * (255 << 8))
415 				form_fg = colors[WHITE];
416 			else
417 				form_fg = colors[BLACK];
418 			/* set same so we don't get white or black when click on color */
419 			NextArg(XtNforeground, x_color(i));
420 			NextArg(XtNbackground, x_color(i));
421 		    /* mono display */
422 		    } else {
423 			if (i == WHITE) {
424 			    NextArg(XtNforeground, x_fg_color.pixel);
425 			    NextArg(XtNbackground, x_bg_color.pixel);
426 			} else {
427 			    NextArg(XtNforeground, x_bg_color.pixel);
428 			    NextArg(XtNbackground, x_fg_color.pixel);
429 			}
430 		    }
431 		    /* no need for label because the color is obvious */
432 		    if (all_colors_available) {
433 			NextArg(XtNlabel, "");
434 		    } else {	/* on a monochrome system give short colornames */
435 			NextArg(XtNlabel, short_clrNames[i+1]);
436 		    }
437 		    NextArg(XtNwidth, STD_COL_W);
438 		} else {				/* it's the default color */
439 		    NextArg(XtNforeground, x_bg_color.pixel);
440 		    NextArg(XtNbackground, x_fg_color.pixel);
441 		    NextArg(XtNlabel, short_clrNames[0]);
442 		    NextArg(XtNwidth, STD_COL_W*2+4);
443 		}
444 		NextArg(XtNfromVert, below);
445 		NextArg(XtNfromHoriz, beside);
446 		NextArg(XtNresize, False);
447 		NextArg(XtNresizable, False);
448 		NextArg(XtNtop, XtChainTop);
449 		NextArg(XtNbottom, XtChainTop);
450 		NextArg(XtNleft, XtChainLeft);
451 		NextArg(XtNright, XtChainLeft);
452 		beside = XtCreateManagedWidget("stdColor", commandWidgetClass,
453 					       stdForm, Args, ArgCount);
454 		XtAddEventHandler(beside, ButtonReleaseMask, False,
455 				  (XtEventHandler) _set_std_color, (XtPointer) choice);
456 		XtOverrideTranslations(beside,
457 			       XtParseTranslationTable(std_color_translations));
458 	}
459 
460 	/********************************/
461 	/* now the extended color panel */
462 	/********************************/
463 
464 	/* make a label for the color memories */
465 
466 	FirstArg(XtNlabel, "User Defined Colors");
467 	NextArg(XtNfromVert, stdForm);
468 	NextArg(XtNfromHoriz, mixedForm[0]);
469 	NextArg(XtNtop, XtChainTop);
470 	NextArg(XtNbottom, XtChainTop);
471 	NextArg(XtNleft, XtChainLeft);
472 	NextArg(XtNright, XtChainLeft);
473 	userLabel = XtCreateManagedWidget("userLabel", labelWidgetClass,
474 						form, Args, ArgCount);
475 
476 	/* label to show count of colors actually in use */
477 
478 	FirstArg(XtNlabel, "x In use");
479 	NextArg(XtNforeground, x_color(GREEN4));
480 	NextArg(XtNfromVert, stdForm);
481 	NextArg(XtNfromHoriz, userLabel);
482 	NextArg(XtNhorizDistance, 25);
483 	NextArg(XtNtop, XtChainTop);
484 	NextArg(XtNbottom, XtChainTop);
485 	NextArg(XtNleft, XtChainLeft);
486 	NextArg(XtNright, XtChainRight);
487 	colorCount = XtCreateManagedWidget("colorCount", labelWidgetClass,
488 						form, Args, ArgCount);
489 
490 	/* wrap the rest (after the label) in a form for looks */
491 
492 	FirstArg(XtNfromVert, userLabel);
493 	NextArg(XtNfromHoriz, mixedForm[0]);
494 	NextArg(XtNvertDistance, 0);
495 	NextArg(XtNborderWidth, 2);
496 	NextArg(XtNtop, XtChainTop);
497 	NextArg(XtNbottom, XtChainBottom);
498 	NextArg(XtNleft, XtChainLeft);
499 	NextArg(XtNright, XtChainRight);
500 	userForm = XtCreateManagedWidget("userForm", formWidgetClass,
501 				form, Args, ArgCount);
502 
503 	/* make a scrollable viewport widget to contain the color memory buttons */
504 
505 	FirstArg(XtNallowVert, True);
506 	NextArg(XtNwidth, isw->sw_per_row*(STD_COL_W+3));
507 	NextArg(XtNheight, (USR_COL_H+USR_COL_SP)*3+22);  /* the 22 is for the scrollbar */
508 	NextArg(XtNborderWidth, 1);
509 	NextArg(XtNforceBars, True);
510 	NextArg(XtNtop, XtChainTop);
511 	NextArg(XtNbottom, XtChainBottom);
512 	NextArg(XtNleft, XtChainLeft);
513 	NextArg(XtNright, XtChainRight);
514 	userViewport = XtCreateManagedWidget("userViewport", viewportWidgetClass,
515 			userForm, Args, ArgCount);
516 
517 	FirstArg(XtNhSpace, USR_COL_SP);		/* spacing between cells */
518 	NextArg(XtNresizable, True);
519 	NextArg(XtNborderWidth, 0);
520 	NextArg(XtNorientation, XtorientVertical);
521 
522 	userBox = XtCreateManagedWidget("userBox", boxWidgetClass, userViewport,
523 			       Args, ArgCount);
524 
525 	/* count the number of user colors actually in use */
526 	count_user_colors();
527 
528 	/* create the color cells */
529 	for (i = 0; i < num_usr_cols; i++) {
530 	    if (!colorFree[i]) {
531 		create_cell(i,user_colors[i]);
532 	    }
533 	}
534 
535 	/* now the add/delete color buttons */
536 
537 	FirstArg(XtNlabel, "Add Color");
538 	NextArg(XtNfromVert, userViewport);
539 	NextArg(XtNtop, XtChainBottom);
540 	NextArg(XtNbottom, XtChainBottom);
541 	NextArg(XtNleft, XtChainLeft);
542 	NextArg(XtNright, XtChainLeft);
543 	addColor = XtCreateManagedWidget("addColor", commandWidgetClass,
544 					       userForm, Args, ArgCount);
545 	XtAddEventHandler(addColor, ButtonReleaseMask, False,
546 			  (XtEventHandler) add_color, (XtPointer) 0);
547 
548 	FirstArg(XtNlabel, "Lookup and Add");
549 	NextArg(XtNfromHoriz, addColor);
550 	NextArg(XtNfromVert, userViewport);
551 	NextArg(XtNtop, XtChainBottom);
552 	NextArg(XtNbottom, XtChainBottom);
553 	NextArg(XtNleft, XtChainLeft);
554 	NextArg(XtNright, XtChainLeft);
555 	lookupColor = XtCreateManagedWidget("lookupColor", commandWidgetClass,
556 					       userForm, Args, ArgCount);
557 	XtAddEventHandler(lookupColor, ButtonReleaseMask, False,
558 			  (XtEventHandler) lookup_color, (XtPointer) 0);
559 
560 	/* start new row of buttons */
561 
562 	FirstArg(XtNlabel, "Delete");
563 	NextArg(XtNfromVert, addColor);
564 	NextArg(XtNtop, XtChainBottom);
565 	NextArg(XtNbottom, XtChainBottom);
566 	NextArg(XtNleft, XtChainLeft);
567 	NextArg(XtNright, XtChainLeft);
568 	delColor = XtCreateManagedWidget("deleteColor", commandWidgetClass,
569 					       userForm, Args, ArgCount);
570 	XtAddEventHandler(delColor, ButtonReleaseMask, False,
571 			  (XtEventHandler) del_color, (XtPointer) 0);
572 
573 	FirstArg(XtNlabel, "UnDelete");
574 	NextArg(XtNfromHoriz, delColor);
575 	NextArg(XtNfromVert, addColor);
576 	NextArg(XtNtop, XtChainBottom);
577 	NextArg(XtNbottom, XtChainBottom);
578 	NextArg(XtNleft, XtChainLeft);
579 	NextArg(XtNright, XtChainLeft);
580 	undelColor = XtCreateManagedWidget("undeleteColor", commandWidgetClass,
581 					       userForm, Args, ArgCount);
582 	XtAddEventHandler(undelColor, ButtonReleaseMask, False,
583 			  (XtEventHandler) undel_color, (XtPointer) 0);
584 
585 	FirstArg(XtNlabel, "Delete Unused");
586 	NextArg(XtNfromHoriz, undelColor);
587 	NextArg(XtNfromVert, addColor);
588 	NextArg(XtNtop, XtChainBottom);
589 	NextArg(XtNbottom, XtChainBottom);
590 	NextArg(XtNleft, XtChainLeft);
591 	NextArg(XtNright, XtChainLeft);
592 	delunusedColors = XtCreateManagedWidget("deleteUnused", commandWidgetClass,
593 					       userForm, Args, ArgCount);
594 	XtAddEventHandler(delunusedColors, ButtonReleaseMask, False,
595 			  (XtEventHandler) del_unused_user_colors, (XtPointer) 0);
596 
597 	/***************************************************************************/
598 	/* now the form with the rgb/hsv scrollbars just below the add/del buttons */
599 	/***************************************************************************/
600 
601 	FirstArg(XtNfromVert, delColor);
602 	NextArg(XtNdefaultDistance, SCROLL_SP);
603 	NextArg(XtNtop, XtChainBottom);
604 	NextArg(XtNbottom, XtChainBottom);
605 	NextArg(XtNleft, XtChainLeft);
606 	NextArg(XtNright, XtChainLeft);
607 	mixingForm = XtCreateManagedWidget("mixingForm", formWidgetClass,
608 						userForm, Args, ArgCount);
609 	XtAppAddActions(XtWidgetToApplicationContext(mixingForm),
610 					actionTable, XtNumber(actionTable));
611 
612 	FirstArg(XtNwidth, SCROLL_W);
613 	NextArg(XtNheight, SCROLL_W);
614 	NextArg(XtNborderWidth, 2);
615 	NextArg(XtNlabel, "");
616 	redLocked = XtCreateManagedWidget("redLocked", labelWidgetClass,
617 						mixingForm, Args, ArgCount);
618 	XtOverrideTranslations(redLocked,
619 			       XtParseTranslationTable(redLocked_translations));
620 	FirstArg(XtNwidth, SCROLL_W);
621 	NextArg(XtNheight, SCROLL_W);
622 	NextArg(XtNborderWidth, 2);
623 	NextArg(XtNlabel, "");
624 	NextArg(XtNfromHoriz, redLocked);
625 	greenLocked = XtCreateManagedWidget("greenLocked", labelWidgetClass,
626 						mixingForm, Args, ArgCount);
627 	XtOverrideTranslations(greenLocked,
628 			       XtParseTranslationTable(greenLocked_translations));
629 	FirstArg(XtNwidth, SCROLL_W);
630 	NextArg(XtNheight, SCROLL_W);
631 	NextArg(XtNborderWidth, 2);
632 	NextArg(XtNlabel, "");
633 	NextArg(XtNfromHoriz, greenLocked);
634 	blueLocked = XtCreateManagedWidget("blueLocked", labelWidgetClass,
635 						mixingForm, Args, ArgCount);
636 	XtOverrideTranslations(blueLocked,
637 			       XtParseTranslationTable(blueLocked_translations));
638 
639 
640 	FirstArg(XtNlabel, "Lock");
641 	NextArg(XtNheight, SCROLL_W);
642 	NextArg(XtNborderWidth, 2);
643 	NextArg(XtNfromHoriz, blueLocked);
644 	lockedLabel = XtCreateManagedWidget("lockedLabel", labelWidgetClass,
645 						mixingForm, Args, ArgCount);
646 	FirstArg(XtNborderWidth, 2);
647 	NextArg(XtNwidth, SCROLL_W);
648 	NextArg(XtNheight, SCROLL_H);
649 	NextArg(XtNthumb, None);
650 	if (all_colors_available) {
651 	    NextArg(XtNforeground, colors[RED]);
652 	    NextArg(XtNborderColor, colors[RED]);
653 	}
654 
655 	NextArg(XtNfromVert, redLocked);
656 	redScroll = XtCreateManagedWidget("redScroll", scrollbarWidgetClass,
657 						mixingForm, Args, ArgCount);
658 	FirstArg(XtNborderWidth, 2);
659 	NextArg(XtNwidth, SCROLL_W);
660 	NextArg(XtNheight, SCROLL_H);
661 	NextArg(XtNthumb, None);
662 	if (all_colors_available) {
663 	    NextArg(XtNforeground, colors[GREEN]);
664 	    NextArg(XtNborderColor, colors[GREEN]);
665 	}
666 	NextArg(XtNfromHoriz, redScroll);
667 	NextArg(XtNfromVert, greenLocked);
668 	greenScroll = XtCreateManagedWidget("greenScroll", scrollbarWidgetClass,
669 						mixingForm, Args, ArgCount);
670 
671 	FirstArg(XtNborderWidth, 2);
672 	NextArg(XtNwidth, SCROLL_W);
673 	NextArg(XtNheight, SCROLL_H);
674 	NextArg(XtNthumb, None);
675 	if (all_colors_available) {
676 	    NextArg(XtNforeground, colors[BLUE]);
677 	    NextArg(XtNborderColor, colors[BLUE]);
678 	}
679 	NextArg(XtNfromHoriz, greenScroll);
680 	NextArg(XtNfromVert, blueLocked);
681 	blueScroll = XtCreateManagedWidget("blueScroll", scrollbarWidgetClass,
682 						mixingForm, Args, ArgCount);
683 	FirstArg(XtNborderWidth, 2);
684 	NextArg(XtNwidth, SCROLL_W);
685 	NextArg(XtNheight, SCROLL_H);
686 	NextArg(XtNthumb, None);
687 	NextArg(XtNfromHoriz, blueScroll);
688 	NextArg(XtNfromVert, blueLocked);
689 	lockedScroll = XtCreateManagedWidget("lockedScroll",
690 				scrollbarWidgetClass, mixingForm, Args, ArgCount);
691 	XtSetSensitive(lockedScroll, False);
692 
693 	FirstArg(XtNlabel, "H");
694 	NextArg(XtNwidth, SCROLL_W);
695 	NextArg(XtNheight, SCROLL_W);
696 	NextArg(XtNborderWidth, 2);
697 	NextArg(XtNfromHoriz, lockedLabel);
698 	hueLabel = XtCreateManagedWidget("hueLabel", labelWidgetClass,
699 						mixingForm, Args, ArgCount);
700 	FirstArg(XtNlabel, "S");
701 	NextArg(XtNwidth, SCROLL_W);
702 	NextArg(XtNheight, SCROLL_W);
703 	NextArg(XtNborderWidth, 2);
704 	NextArg(XtNfromHoriz, hueLabel);
705 	satLabel = XtCreateManagedWidget("satLabel", labelWidgetClass,
706 						mixingForm, Args, ArgCount);
707 	FirstArg(XtNlabel, "V");
708 	NextArg(XtNwidth, SCROLL_W);
709 	NextArg(XtNheight, SCROLL_W);
710 	NextArg(XtNborderWidth, 2);
711 	NextArg(XtNfromHoriz, satLabel);
712 	valLabel = XtCreateManagedWidget("valLabel", labelWidgetClass,
713 						mixingForm, Args, ArgCount);
714 	FirstArg(XtNborderWidth, 2);
715 	NextArg(XtNwidth, SCROLL_W);
716 	NextArg(XtNheight, SCROLL_H);
717 	NextArg(XtNthumb, None);
718 	NextArg(XtNfromHoriz, lockedLabel);
719 	NextArg(XtNfromVert, hueLabel);
720 	hueScroll = XtCreateManagedWidget("hueScroll", scrollbarWidgetClass,
721 						mixingForm, Args, ArgCount);
722 	FirstArg(XtNborderWidth, 2);
723 	NextArg(XtNwidth, SCROLL_W);
724 	NextArg(XtNheight, SCROLL_H);
725 	NextArg(XtNthumb, None);
726 	NextArg(XtNfromHoriz, hueScroll);
727 	NextArg(XtNfromVert, satLabel);
728 	satScroll = XtCreateManagedWidget("satScroll", scrollbarWidgetClass,
729 						mixingForm, Args, ArgCount);
730 	FirstArg(XtNborderWidth, 2);
731 	NextArg(XtNwidth, SCROLL_W);
732 	NextArg(XtNheight, SCROLL_H);
733 	NextArg(XtNthumb, None);
734 	NextArg(XtNfromHoriz, satScroll);
735 	NextArg(XtNfromVert, valLabel);
736 	valScroll = XtCreateManagedWidget("valScroll", scrollbarWidgetClass,
737 						mixingForm, Args, ArgCount);
738 
739 	/* get background color of redLocked to restore background for locked sliders */
740 	XtVaGetValues(redLocked, XtNbackground, &(original_background), NULL);
741 	bars_locked = 0;
742 
743 	XtAddCallback(redScroll,    XtNjumpProc, Thumbed, (XtPointer)S_RED);
744 	XtAddCallback(greenScroll,  XtNjumpProc, Thumbed, (XtPointer)S_GREEN);
745 	XtAddCallback(blueScroll,   XtNjumpProc, Thumbed, (XtPointer)S_BLUE);
746 	XtAddCallback(lockedScroll, XtNjumpProc, Thumbed, (XtPointer)S_LOCKED);
747 
748 	XtAddCallback(redScroll,    XtNscrollProc, Scrolled, (XtPointer)S_RED);
749 	XtAddCallback(greenScroll,  XtNscrollProc, Scrolled, (XtPointer)S_GREEN);
750 	XtAddCallback(blueScroll,   XtNscrollProc, Scrolled, (XtPointer)S_BLUE);
751 	XtAddCallback(lockedScroll, XtNscrollProc, Scrolled, (XtPointer)S_LOCKED);
752 	XtAddCallback(hueScroll,    XtNscrollProc, Scrolled, (XtPointer)S_HUE);
753 	XtAddCallback(satScroll,    XtNscrollProc, Scrolled, (XtPointer)S_SAT);
754 	XtAddCallback(valScroll,    XtNscrollProc, Scrolled, (XtPointer)S_VAL);
755 
756 	XtAddCallback(hueScroll,    XtNjumpProc, Update_HSV, (XtPointer)S_HUE);
757 	XtAddCallback(satScroll,    XtNjumpProc, Update_HSV, (XtPointer)S_SAT);
758 	XtAddCallback(valScroll,    XtNjumpProc, Update_HSV, (XtPointer)S_VAL);
759 
760 	/* initialize the two color cells to the current pen/fill colors */
761 	restore_mixed_colors();
762 
763 	/* get the name of the scrollbar in the user color viewport so we can
764 	   make it solid instead of the default grey pixmap */
765 	sb = XtNameToWidget(userViewport, "vertical");
766 	FirstArg(XtNthumb, None);
767 	SetValues(sb);
768 
769 	XtPopup(choice_popup, (appres.DEBUG? XtGrabNone: XtGrabExclusive));
770 	XtInstallAccelerators(form, cancel);
771 
772 	/* if the file message window is up add it to the grab */
773 	file_msg_add_grab();
774 
775 	(void) XSetWMProtocols(tool_d, XtWindow(choice_popup), &wm_delete_window, 1);
776 	/* insure that the most recent colormap is installed */
777 	set_cmap(XtWindow(choice_popup));
778 
779 	/* activate pen or fill depending on which button was pressed to pop us up */
780 	pen_fill_activate(isw->func);
781 
782 	/* don't know why this is necessary, but the "Lock" label is insensitive otherwise */
783 	XtSetSensitive(lockedLabel, True);
784 
785 	/* inactivate the delete color button until user clicks on colorcell */
786 	XtSetSensitive(delColor, False);
787 	/* and the undelete color button until user deletes a color */
788 	XtSetSensitive(undelColor, False);
789 
790 	/* and the "delete unused colors" button if there are no user colors */
791 	if (num_usr_cols == 0) {
792 	    XtSetSensitive(delunusedColors, False);
793 	}
794 
795 	/* make sliders insensitive if the selected color (fill or pen)
796 	   is not a user color */
797 
798 	set_slider_sensitivity();
799 }
800 
pen_fill_activate(int func)801 void pen_fill_activate(int func)
802 {
803 	/* make sliders insensitive if the selected color (fill or pen)
804 	   is not a user color */
805 
806 	set_slider_sensitivity();
807 
808 	/* activate the one the user pressed (pen or fill) */
809 	XawToggleSetCurrent(mixedEdit[0],(XtPointer) (intptr_t)(func==I_PEN_COLOR? 1:2));
810 }
811 
restore_mixed_colors(void)812 void restore_mixed_colors(void)
813 {
814 	int	save0,save1,save_edit;
815 
816 	/* initialize the two color cells to the current pen/fill colors */
817 	save0 = mixed_color[0].pixel;
818 	save1 = mixed_color[1].pixel;
819 
820 	mixed_color[0].pixel = x_color(cur_pencolor);
821 	mixed_color[1].pixel = x_color(cur_fillcolor);
822 	XQueryColors(tool_d, tool_cm, mixed_color, 2);
823 	/* keep lower 8 bits 0 */
824 	mixed_color[0].red &= 0xff00;
825 	mixed_color[0].green &= 0xff00;
826 	mixed_color[0].blue &= 0xff00;
827 	mixed_color[1].red &= 0xff00;
828 	mixed_color[1].green &= 0xff00;
829 	mixed_color[1].blue &= 0xff00;
830 
831 	/* put the color name or number in the indicators */
832 	set_mixed_name(0, cur_pencolor);
833 	set_mixed_name(1, cur_fillcolor);
834 	if (!all_colors_available) {
835 	    if (mixedColor[0] != (Widget) 0) {
836 		/* change the background of the widgets */
837 		FirstArg(XtNbackground,
838 			(cur_pencolor==WHITE? x_bg_color.pixel: x_fg_color.pixel));
839 		SetValues(mixedColor[0]);
840 		FirstArg(XtNbackground,
841 			(cur_fillcolor==WHITE? x_bg_color.pixel: x_fg_color.pixel));
842 		SetValues(mixedColor[1]);
843 	    }
844 	    return;
845 	}
846 	mixed_color[0].pixel = save0;
847 	mixed_color[1].pixel = save1;
848 	/* now change the background of the widgets if StaticGray, StaticColor,
849 	   DirectColor or TrueColor visuals */
850 	set_mixed_color(0);
851 	set_mixed_color(1);
852 
853 	/* set the scrollbars to the initial mixed colour values */
854 	do_change = False;
855 	CHANGE_RED(edit_fill-2);
856 	CHANGE_GREEN(edit_fill-2);
857 	CHANGE_BLUE(edit_fill-2);
858 	do_change = True;
859 
860 	/* and update the hex values */
861 	save_edit = edit_fill;
862 	edit_fill=0;
863 	update_triple();
864 	edit_fill=1;
865 	update_triple();
866 	edit_fill = save_edit;
867 }
868 
869 /*
870    For the StaticGray, StaticColor, DirectColor and TrueColor visual classes
871    change the background color of the mixed color widget specified by which
872    (0=pen, 1=fill).  For the other classes, we changed the color for the
873    allocated pixel when we called YStoreColor().
874 */
875 
set_mixed_color(int which)876 void set_mixed_color(int which)
877 {
878 	if (!all_colors_available)
879 	    return;
880 
881 	if( tool_vclass == StaticGray ||
882 	    tool_vclass == StaticColor ||
883 	    tool_vclass == DirectColor ||
884 	    tool_vclass == TrueColor) {
885 		XAllocColor(tool_d, tool_cm, &mixed_color[which]);
886 		FirstArg(XtNbackground, mixed_color[which].pixel);
887 		SetValues(mixedColor[which]);
888 	} else {
889 		XStoreColor(tool_d, tool_cm, &mixed_color[which]);
890 	}
891 	/* now make contrasting color for the label */
892 	if (colorMemory[which]) {
893 	    pick_contrast(mixed_color[which],mixedColor[which]);
894 	}
895 }
896 
897 /* This is similar to set_mixed_color() except it is for the user colors */
898 
set_user_color(int which)899 void set_user_color(int which)
900 {
901 	if (!all_colors_available)
902 	    return;
903 
904 	if( tool_vclass == StaticGray ||
905 	    tool_vclass == StaticColor ||
906 	    tool_vclass == DirectColor ||
907 	    tool_vclass == TrueColor) {
908 		XAllocColor(tool_d, tool_cm, &user_colors[which]);
909 		/* update colors pixel value */
910 		colors[which+NUM_STD_COLS] = user_colors[which].pixel;
911 		if (colorMemory[which]) {
912 		    FirstArg(XtNbackground, user_colors[which].pixel);
913 		    SetValues(colorMemory[which]);
914 		}
915 	} else {
916 		XStoreColor(tool_d, tool_cm, &user_colors[which]);
917 	}
918 	/* make the label in a contrasting color */
919 	if (colorMemory[which]) {
920 	    pick_contrast(user_colors[which],colorMemory[which]);
921 	}
922 }
923 
pick_contrast(XColor color,Widget widget)924 void pick_contrast(XColor color, Widget widget)
925 {
926     Pixel cell_fg;
927     if ((0.30 * color.red +
928 	 0.59 * color.green +
929 	 0.11 * color.blue) < 0.5 * (255 << 8))
930 	    cell_fg = colors[WHITE];
931     else
932 	    cell_fg = colors[BLACK];
933     FirstArg(XtNforeground, cell_fg);
934     SetValues(widget);
935 }
936 
937 /* change the label of the mixedColor widget[i] to the name of the color */
938 /* also set the foreground color to contrast the background */
939 
set_mixed_name(int i,int col)940 void set_mixed_name(int i, int col)
941 {
942     int	fore;
943     char	buf[20];
944 
945     if (col < NUM_STD_COLS) {
946 	FirstArg(XtNlabel, colorNames[col+1].name);
947 	if (col == WHITE)
948 		fore = x_fg_color.pixel;
949 	else
950 		fore = x_bg_color.pixel;
951     } else {
952 	sprintf(buf,"User %d",col);
953 	FirstArg(XtNlabel, buf);
954 	fore = x_bg_color.pixel;
955     }
956     /* make contrasting foreground (text) color */
957     NextArg(XtNforeground, fore);
958     SetValues(mixedColor[i]);
959 }
960 
961 /* come here when cancel is pressed.  This is in addition to the
962    cancel callback routine that gets called in w_indpanel.c */
963 
964 static void
cancel_color_popup(Widget w,XtPointer closure,XtPointer call_data)965 cancel_color_popup(Widget w, XtPointer closure, XtPointer call_data)
966 {
967 	/* restore the mixed color panels */
968 	restore_mixed_colors();
969 }
970 
971 /* add a color memory cell to the user colors */
972 
973 static void
add_color(Widget w,XtPointer closure,XtPointer call_data)974 add_color(Widget w, XtPointer closure, XtPointer call_data)
975 {
976 	/* allow user to delete unused colors */
977 	XtSetSensitive(delunusedColors, True);
978 	/* deselect any cell currently selected */
979 	erase_boxed(current_memory);
980 	/* add another widget to the user color panel */
981 	if ((current_memory = add_color_cell(DONT_USE_EXISTING_COLOR, 0, 0, 0, 0)) == -1)
982 		put_msg("No more user colors allowed");
983 	modified[edit_fill] = True;
984 	pick_memory(current_memory);
985 	colorUsed[current_memory]=True;
986 
987 }
988 
989 /* delete a color memory (current_memory) from the user colors */
990 
991 static void
del_color(Widget w,XtPointer closure,XtPointer call_data)992 del_color(Widget w, XtPointer closure, XtPointer call_data)
993 {
994 	int save_mem, save_edit;
995 	int i;
996 	if (current_memory == -1 || num_usr_cols <= 0) {
997 		beep();
998 		return;
999 	}
1000 	/* only allow deletion of this color of no object in the figure uses it */
1001 	if (color_used(current_memory+NUM_STD_COLS, &objects)) {
1002 		put_msg("That color is in use by an object in the figure");
1003 		beep();
1004 		return;
1005 	}
1006 	/* get rid of the box drawn around this cell */
1007 	erase_boxed(current_memory);
1008 	/* save it to undelete */
1009 	undel_user_color = user_colors[current_memory];
1010 	del_color_cell(current_memory);
1011 	/* inactivate the delete color button until user clicks on colorcell */
1012 	XtSetSensitive(delColor, False);
1013 	/* and activate the undelete button */
1014 	XtSetSensitive(undelColor, True);
1015 	/* change the current pen/fill color to default if we just deleted that color */
1016 	save_mem = current_memory;
1017 	save_edit = edit_fill;
1018 	/* set_std_color() sets current_memory to -1 when finished (which we want) */
1019 	for (i=0; i<2; i++) {
1020 	    if (mixed_color_indx[i] == save_mem+NUM_STD_COLS) {
1021 		edit_fill = i;
1022 		set_std_color(DEFAULT);
1023 	    }
1024 	}
1025 	edit_fill = save_edit;
1026 }
1027 
1028 /* undelete the last user color deleted */
1029 
1030 static void
undel_color(Widget w,XtPointer closure,XtPointer call_data)1031 undel_color(Widget w, XtPointer closure, XtPointer call_data)
1032 {
1033 	int	    indx;
1034 
1035 	XtSetSensitive(undelColor, False);
1036 	if ((indx=add_color_cell(DONT_USE_EXISTING_COLOR, 0, undel_user_color.red/256,
1037 		undel_user_color.green/256,
1038 		undel_user_color.blue/256)) == -1) {
1039 		    put_msg("Can't allocate more than %d user colors, not enough colormap entries",
1040 				num_usr_cols);
1041 		    return;
1042 		}
1043 	colorUsed[indx] = True;
1044 }
1045 
1046 /* count the number of unique user colors actually used by Fig objects */
1047 /* this is called when the user pops up the color panel */
1048 
1049 void
count_user_colors(void)1050 count_user_colors(void)
1051 {
1052     F_compound  *c;
1053     int		 i, count;
1054     char	 buf[20];
1055 
1056     /* keep array of counts of each color */
1057     for (i=0; i<num_usr_cols; i++)
1058 	colors_used[i] = 0;
1059 
1060     /* count colors in main list */
1061     c_user_colors(&objects);
1062 
1063     /* now any "next" compounds off the top of the list */
1064     for (c = objects.next; c != NULL; c = c->next) {
1065 	c_user_colors(c);
1066     }
1067     /* now add up the colors that are used */
1068     count = 0;
1069     for (i=0; i<num_usr_cols; i++)
1070 	if (colors_used[i])
1071 	    count++;
1072     sprintf(buf,"%3d In use",count);
1073     FirstArg(XtNlabel, buf);
1074     SetValues(colorCount);
1075 }
1076 
c_user_colors(F_compound * obj)1077 void c_user_colors(F_compound *obj)
1078 {
1079     F_arc	   *a;
1080     F_ellipse	   *e;
1081     F_line	   *l;
1082     F_spline	   *s;
1083     F_text	   *t;
1084     F_compound	   *c;
1085 
1086     /* traverse the compounds in this compound */
1087     for (c = obj->compounds; c != NULL; c = c->next) {
1088 	c_user_colors(c);
1089     }
1090     /* now the other objects */
1091     for (a = obj->arcs; a != NULL; a = a->next) {
1092 	count_one(a->pen_color);
1093 	count_one(a->fill_color);
1094     }
1095     for (e = obj->ellipses; e != NULL; e = e->next) {
1096 	count_one(e->pen_color);
1097 	count_one(e->fill_color);
1098     }
1099     for (l = obj->lines; l != NULL; l = l->next) {
1100 	count_one(l->pen_color);
1101 	count_one(l->fill_color);
1102     }
1103     for (s = obj->splines; s != NULL; s = s->next) {
1104 	count_one(s->pen_color);
1105 	count_one(s->fill_color);
1106     }
1107     for (t = obj->texts; t != NULL; t = t->next) {
1108 	count_one(t->color);
1109     }
1110 }
1111 
count_one(int color)1112 void count_one(int color)
1113 {
1114    if (color >= NUM_STD_COLS)
1115 	colors_used[color-NUM_STD_COLS] = 1;
1116 }
1117 
1118 /* delete unused user colors */
1119 
1120 static void
del_unused_user_colors(void)1121 del_unused_user_colors(void)
1122 {
1123     int		 i, save_mem;
1124     Boolean	 deleted = False;
1125 
1126     save_mem = -1;
1127     /* first unmanage the Box that the color cells are in */
1128     XtUnmanageChild(userBox);
1129     for (i=0; i<num_usr_cols; i++)
1130 	if (colors_used[i] == 0 && !colorFree[i]) {
1131 	    deleted = True;
1132 	    /* save it to undelete */
1133 	    undel_user_color = user_colors[i];
1134 	    del_color_cell(i);
1135 	    /* if user deletes selected cell */
1136 	    if (i==current_memory) {
1137 		save_mem = current_memory;
1138 		current_memory = -1;
1139 	    }
1140 	}
1141     /* remanage the Box */
1142     XtManageChild(userBox);
1143 
1144     /* activate the undelete button if any were deleted */
1145     if (deleted)
1146 	XtSetSensitive(undelColor, True);
1147 
1148     /* if we deleted the selected cell, but another one exists, select it */
1149     if (save_mem >= 0) {
1150 	for (i=0; i<num_usr_cols; i++) {
1151 	    if (!colorFree[i]) {
1152 		current_memory = i;
1153 		pick_memory(i);
1154 		break;
1155 	    }
1156 	}
1157 	/* if we didn't find any other user colors, select DEFAULT */
1158 	if (i>=num_usr_cols)
1159 	    set_std_color(DEFAULT);
1160     } else {
1161 	/* make sliders insensitive if we deleted the selected color */
1162 	set_slider_sensitivity();
1163     }
1164 }
1165 
1166 /* lookup color from another window - the user clicks the mouse
1167    on a color and a new cell is created with that color */
1168 
1169 static void
lookup_color(Widget w,XtPointer closure,XtPointer call_data)1170 lookup_color(Widget w, XtPointer closure, XtPointer call_data)
1171 {
1172     Colormap	cmap;
1173     Pixel	p;
1174     XColor	xcol;
1175 
1176     /* grab the server to get a pixel from a window */
1177     DoGrabPixel(w, &p, &cmap);
1178     /* get its rgb values */
1179     xcol.pixel = p;
1180     xcol.flags = DoRed | DoGreen | DoBlue;
1181     XQueryColor(tool_d, cmap, &xcol);
1182 
1183     /* make a new cell and set current_memory to that */
1184     add_color(w, closure, call_data);
1185 
1186     /* and store the chosen color (keep lower 8 bits 0) */
1187     user_colors[current_memory].red   = xcol.red   & 0xff00;
1188     user_colors[current_memory].green = xcol.green & 0xff00;
1189     user_colors[current_memory].blue  = xcol.blue  & 0xff00;
1190     user_colors[current_memory].flags = DoRed|DoGreen|DoBlue;
1191     set_user_color(current_memory);
1192     pick_memory(current_memory);
1193 }
1194 
1195 /* add a widget to the user color list with color r,g,b */
1196 /* call with use_exist true if you wish to allocate cell <indx> explicitly */
1197 /* also increment num_usr_cols if we add a colorcell beyond the current number */
1198 /* return the colorcell number (0..MAX_USR_COLS-1) */
1199 /* return -1 if MAX_USR_COLS already used */
1200 
1201 int
add_color_cell(Boolean use_exist,int indx,int r,int g,int b)1202 add_color_cell(Boolean use_exist, int indx, int r, int g, int b)
1203 {
1204 	int	i;
1205 	Boolean	new;
1206 
1207 	if (all_colors_available) {
1208 	    /* try to get a colorcell */
1209 	    if (!alloc_color_cells(pixels,1)) {
1210 		put_msg("Can't allocate user color, not enough colorcells");
1211 		return -1;
1212 	    }
1213 	}
1214 	if (!use_exist) {
1215 		/* first look for color cell available in the middle */
1216 		new = False;
1217 		for (i=0; i<num_usr_cols; i++)
1218 		    if (colorFree[i])
1219 			break;
1220 		indx = i;
1221 	}
1222 
1223 	/* if a space is free but there was never a color there, must create one */
1224 	if (indx < num_usr_cols && colorMemory[indx]==0)
1225 	    new = True;
1226 
1227 	/* if not, increment num_usr_cols */
1228 	if (indx >= num_usr_cols) {
1229 	    if (num_usr_cols >= MAX_USR_COLS)
1230 		return -1;
1231 	    if (use_exist)
1232 		num_usr_cols = indx+1;
1233 	    else
1234 		num_usr_cols++;
1235 	    new = True;
1236 	}
1237 
1238 	user_colors[indx].red = r*256;
1239 	user_colors[indx].green = g*256;
1240 	user_colors[indx].blue = b*256;
1241 	user_colors[indx].flags = DoRed|DoGreen|DoBlue;
1242 	user_colors[indx].pixel = pixels[0];
1243 	/* in case we have read-only colormap, get the pixel value now */
1244 	if (all_colors_available)
1245 	    YStoreColor(tool_cm,&user_colors[indx]);
1246 	/* and put it in main colors */
1247 	colors[NUM_STD_COLS+indx] = user_colors[indx].pixel;
1248 
1249 	colorFree[indx] = False;
1250 	colorUsed[indx] = False;
1251 
1252 
1253 	/* if the color popup has been created create the widgets */
1254 	if (pen_color_button->panel) {
1255 	    if (new) {
1256 		/* if new color is at the end of the list, just create it now */
1257 		if (indx == num_usr_cols-1) {
1258 		   create_cell(indx, user_colors[indx]);
1259 		} else {
1260 		    /* otherwise, delete all after it and recreate them */
1261 		    /* first unmanage the Box that they're in */
1262 		    XtUnmanageChild(userBox);
1263 		    for (i=indx+1; i<num_usr_cols; i++)
1264 			if (!colorFree[i])
1265 			    XtDestroyWidget(colorMemory[i]);
1266 		    /* now add the new and the old ones back in */
1267 		    for (i=indx; i<num_usr_cols; i++)
1268 			if (!colorFree[i])
1269 			    create_cell(i, user_colors[indx]);
1270 		    /* remanage the Box */
1271 		    XtManageChild(userBox);
1272 		}
1273 	    } else {
1274 		/* already exists, just set its color and map it */
1275 		FirstArg(XtNforeground, x_bg_color.pixel);
1276 		NextArg(XtNbackground, (all_colors_available?
1277 			user_colors[indx].pixel: x_fg_color.pixel));
1278 		SetValues(colorMemory[indx]);
1279 		XtManageChild(colorMemory[indx]);
1280 	    }
1281 	}
1282 	/* now set the color of the widget if TrueColor, etc. */
1283 	set_user_color(indx);
1284 
1285 	return indx;
1286 }
1287 
1288 /* Create a color cell [indx] with background "color" */
1289 /* Color its border green if it is in use or black otherwise. */
1290 /* NOTE: If we are being called from swap_colors(), the colors_used[]
1291 	 array is probably not correct, but the color_borders() proc
1292 	 is called after this anyway. */
1293 
create_cell(int indx,XColor color)1294 void create_cell(int indx, XColor color)
1295 {
1296     char	labl[5];
1297 
1298     /* put the user color number in all the time */
1299     sprintf(labl,"%d", indx+NUM_STD_COLS);
1300     colorMemory[indx] = XtVaCreateManagedWidget("colorMemory",
1301 	labelWidgetClass, userBox,
1302 	XtNlabel, labl,
1303 	XtNforeground, x_bg_color.pixel,
1304 	XtNbackground, (all_colors_available?
1305 		user_colors[indx].pixel: x_fg_color.pixel),
1306 	XtNwidth, USR_COL_W, XtNheight, USR_COL_H,
1307 	XtNborder, (colors_used[indx]? x_color(GREEN):x_color(BLACK)),
1308 	XtNborderWidth, 2,
1309 	NULL);
1310     XtOverrideTranslations(colorMemory[indx],
1311 		XtParseTranslationTable(user_color_translations));
1312     /* pick contrasting color for label */
1313     pick_contrast(user_colors[indx],colorMemory[indx]);
1314 }
1315 
1316 /*
1317    allocate n colormap entries.  If not enough cells are available, switch
1318    to a private colormap.  If we are already using a private colormap return
1319    False.  The pixel numbers allocated are returned in the Pixel array pixels[]
1320    The new colormap (if used) is set into the main (tool) window and the color
1321    popup panel (if it exists)
1322 */
1323 
1324 Boolean
alloc_color_cells(Pixel * pixels,int n)1325 alloc_color_cells(Pixel *pixels, int n)
1326 {
1327     int	i;
1328 
1329     /* allocate them one at a time in case we can't allocate all of them.
1330        If that is the case then we switch colormaps and allocate the rest */
1331     for (i=0; i<n; i++) {
1332       switch( tool_vclass ){
1333 /*
1334  * Use XAllocColorCells "to allocate read/write color cells and color plane
1335  * combinations for GrayScale and PseudoColor models"
1336  */
1337 	case GrayScale:
1338 	case PseudoColor:
1339 	    if (!XAllocColorCells(tool_d, tool_cm, 0, &plane_masks, 0, &pixels[i], 1)) {
1340 	        /* try again with new colormap */
1341 		if (!switch_colormap() ||
1342 	            (!XAllocColorCells(tool_d, tool_cm, 0, &plane_masks, 0, &pixels[i], 1))) {
1343 		    put_msg("Cannot define user colors.");
1344 		    return False;
1345 		}
1346 	    }
1347 	    break;
1348 /*
1349  * Do not attempt to allocate color resources for static visuals
1350  * (but assume the colors can be achieved).
1351  */
1352 	case StaticGray:
1353 	case StaticColor:
1354 	case DirectColor:
1355 	case TrueColor:
1356 	    break;
1357 
1358         default:
1359 	    put_msg("Cannot define user colors in the Unknown Visual.");
1360 	    return False;
1361       }
1362     }
1363     return True;
1364 }
1365 
1366 /* switch colormaps to private one and reallocate the colors we already used. */
1367 /* Return False if already switched or if can't allocate new colormap */
1368 
1369 Boolean
switch_colormap(void)1370 switch_colormap(void)
1371 {
1372 	if (swapped_cmap || appres.dontswitchcmap) {
1373 	    return False;
1374 	}
1375 	if ((newcmap = XCopyColormapAndFree(tool_d, tool_cm)) == 0) {
1376 	    file_msg("Cannot allocate new colormap.");
1377 	    return False;
1378 	}
1379 	/* swap colormaps */
1380 	tool_cm = newcmap;
1381 	swapped_cmap = True;
1382 	/* and tell the window manager to install it */
1383 	if (pen_color_button && pen_color_button->panel &&
1384 	    XtWindow(pen_color_button->panel) != 0)
1385 		set_cmap(XtWindow(pen_color_button->panel));
1386 	if (tool_w)
1387 	    set_cmap(tool_w);
1388 	file_msg("Switching to private colormap.");
1389 	return True;
1390 }
1391 
1392 
1393 /* delete a color memory cell (indx) from the widgets and arrays */
1394 
del_color_cell(int indx)1395 void del_color_cell(int indx)
1396 {
1397 	unsigned long   pixels[MAX_USR_COLS];
1398 
1399 	/* if already free, just return */
1400 	if (colorFree[indx])
1401 		return;
1402 	/* if the color popup has been created unmanage the widget */
1403 	if (pen_color_button->panel)
1404 	    XtUnmanageChild(colorMemory[indx]);
1405 
1406 	/* free up this colormap entry */
1407 	pixels[0] = user_colors[indx].pixel;
1408 	if (all_colors_available)
1409 	    XFreeColors(tool_d, tool_cm, pixels, 1, 0);
1410 
1411 	/* now set free flag for that cell */
1412 	colorFree[indx] = True;
1413 	colorUsed[indx] = False;
1414 }
1415 
1416 /* if any object in the figure uses the user color "color" return True */
1417 
1418 static Boolean
color_used(int color,F_compound * list)1419 color_used(int color, F_compound *list)
1420 {
1421     F_arc	   *a;
1422     F_text	   *t;
1423     F_compound	   *c;
1424     F_ellipse	   *e;
1425     F_line	   *l;
1426     F_spline	   *s;
1427 
1428     for (a = list->arcs; a != NULL; a = a->next)
1429 	if (a->fill_color == color || a->pen_color == color)
1430 	    return True;
1431     for (t = list->texts; t != NULL; t = t->next)
1432 	if (t->color == color)
1433 	    return True;
1434     for (c = list->compounds; c != NULL; c = c->next)
1435 	if (color_used(color,c))
1436 	    return True;
1437     for (e = list->ellipses; e != NULL; e = e->next)
1438 	if (e->fill_color == color || e->pen_color == color)
1439 	    return True;
1440     for (l = list->lines; l != NULL; l = l->next)
1441 	if (l->fill_color == color || l->pen_color == color)
1442 	    return True;
1443     for (s = list->splines; s != NULL; s = s->next)
1444 	if (s->fill_color == color || s->pen_color == color)
1445 	    return True;
1446     return False;
1447 }
1448 
1449 /* come here when either the edit pen or edit fill button is pressed */
1450 static void
switch_edit(Widget w,XtPointer client_data,XtPointer call_data)1451 switch_edit(Widget w, XtPointer client_data, XtPointer call_data)
1452 {
1453 	edit_fill = (intptr_t) XawToggleGetCurrent(mixedEdit[0]) - 1;
1454 	/* sometimes XawToggleGetCurrent() returns 0 if the
1455 	   toggle hasn't been set manually */
1456 	if (edit_fill == -1)
1457 		edit_fill = 1;
1458 	/* only make triple value sensitive if a user color */
1459 	if (mixed_color_indx[edit_fill] >= NUM_STD_COLS) {
1460 	    XtSetSensitive(tripleValue[edit_fill], True);
1461 	    XtSetSensitive(tripleValue[1-edit_fill], False);
1462 	} else {
1463 	    XtSetSensitive(tripleValue[edit_fill], False);
1464 	    XtSetSensitive(tripleValue[1-edit_fill], False);
1465 	}
1466 
1467 	/* set the scrollbars to the current mixed colour values */
1468 	do_change = False;
1469 	CHANGE_RED(edit_fill-2);
1470 	CHANGE_GREEN(edit_fill-2);
1471 	CHANGE_BLUE(edit_fill-2);
1472 	do_change = True;
1473 
1474 	/* but make them insensitive if the color is not a user defined color */
1475 	set_slider_sensitivity();
1476 }
1477 
1478 /* if the color for the current mode (fill or pen) is a user-defined color then
1479    make the color sliders sensitive, otherwise make them insensitive */
1480 
set_slider_sensitivity(void)1481 void set_slider_sensitivity(void)
1482 {
1483 	if (mixed_color_indx[edit_fill] < NUM_STD_COLS)
1484 		XtSetSensitive(mixingForm, False);
1485 	else
1486 		XtSetSensitive(mixingForm, True);
1487 }
1488 
1489 /* ok button */
1490 
1491 static void
set_color_ok(Widget w,char * dum,XButtonEvent * ev,Boolean disp)1492 set_color_ok(Widget w, char *dum, XButtonEvent *ev, Boolean disp)
1493 {
1494 	int	i,indx;
1495 
1496 	/* put the color pixel value in the table */
1497 	/* this is done here because if the visual is TrueColor, etc. the value of
1498 	   the pixel changes with the color itself */
1499 	for (i=0; i<=1; i++) {
1500 	    indx = mixed_color_indx[i];
1501 	    if (indx >= NUM_STD_COLS)
1502 		colors[indx] = user_colors[indx-NUM_STD_COLS].pixel;
1503 	}
1504 
1505 	/* have either the fill or pen color been modified? */
1506 
1507 	if (modified[0]) {
1508 	    cur_pencolor = mixed_color_indx[0];
1509 	    show_pencolor(); /* update the button in the indicator panel */
1510 	}
1511 	if (modified[1]) {
1512 	    cur_fillcolor = mixed_color_indx[1];
1513 	    show_fillcolor(); /* update the button in the indicator panel */
1514 	}
1515 	modified[0] = modified[1] = False;
1516 	choice_panel_dismiss();
1517 }
1518 
1519 /* set standard color in mixedColor */
1520 
1521 static void
_set_std_color(Widget w,choice_info * sel_choice,XButtonEvent * ev)1522 _set_std_color(Widget w, choice_info *sel_choice, XButtonEvent *ev)
1523 {
1524 	set_std_color(sel_choice->value);
1525 }
1526 
1527 static void
set_std_color(int color)1528 set_std_color(int color)
1529 {
1530 	Pixel	save;
1531 
1532 	/* make sliders insensitive */
1533 	XtSetSensitive(mixingForm, False);
1534 
1535 	/* set flag saying we've modified either the pen or fill color */
1536 	modified[edit_fill] = True;
1537 
1538 	mixed_color_indx[edit_fill] = color;
1539 
1540 	save = mixed_color[edit_fill].pixel;
1541 	mixed_color[edit_fill].pixel = x_color(mixed_color_indx[edit_fill]);
1542 	/* put the colorname in the indicator */
1543 	set_mixed_name(edit_fill,color);
1544 	/* look up color rgb values given the pixel number */
1545 	if (all_colors_available) {
1546 	    XQueryColor(tool_d, tool_cm, &mixed_color[edit_fill]);
1547 	    /* keep lower 8 bits 0 */
1548 	    mixed_color[edit_fill].red &= 0xff00;
1549 	    mixed_color[edit_fill].green &= 0xff00;
1550 	    mixed_color[edit_fill].blue &= 0xff00;
1551 	} else {
1552 	    /* look up color rgb values from the name */
1553 	    if (color == DEFAULT) {
1554 		mixed_color[edit_fill].red = x_bg_color.red;
1555 		  mixed_color[edit_fill].green = x_bg_color.green;
1556 		  mixed_color[edit_fill].blue = x_bg_color.blue;
1557 	    } else {
1558 		XParseColor(tool_d, tool_cm,
1559 			colorNames[color+1].rgb, &mixed_color[edit_fill]);
1560 	    }
1561 	    /* now change the background of the widget */
1562 	    if (color == WHITE)
1563 		FirstArg(XtNbackground, x_bg_color.pixel);
1564 	    else
1565 		FirstArg(XtNbackground, x_fg_color.pixel);
1566 	    SetValues(mixedColor[edit_fill]);
1567 	}
1568 	mixed_color[edit_fill].pixel = save;
1569 
1570 	/* get the rgb values for that color */
1571 	rgb_values[edit_fill].r = mixed_color[edit_fill].red;
1572 	rgb_values[edit_fill].g = mixed_color[edit_fill].green;
1573 	rgb_values[edit_fill].b = mixed_color[edit_fill].blue;
1574 	set_mixed_color(edit_fill);
1575 
1576 	/* undraw any box around the current user-memory cell */
1577 	erase_boxed(current_memory);
1578 	/* update the mixedColor stuff and scrollbars */
1579 	XawScrollbarSetThumb(redScroll,
1580 			(float)(1.0 - rgb_values[edit_fill].r/65536.0), THUMB_H);
1581 	XawScrollbarSetThumb(greenScroll,
1582 			(float)(1.0 - rgb_values[edit_fill].g/65536.0), THUMB_H);
1583 	XawScrollbarSetThumb(blueScroll,
1584 			(float)(1.0 - rgb_values[edit_fill].b/65536.0), THUMB_H);
1585 	update_triple();
1586 	/* inactivate the current_memory cell */
1587 	current_memory = -1;
1588 	/* and the hexadecimal window */
1589 	XtSetSensitive(tripleValue[edit_fill], False);
1590 	/* and the delete color button until user clicks on colorcell */
1591 	XtSetSensitive(delColor, False);
1592 }
1593 
1594 /* make a user color cell active */
1595 
1596 static void
_pick_memory(Widget w,XEvent * event,String * params,Cardinal * num_params)1597 _pick_memory(Widget w, XEvent *event, String *params, Cardinal *num_params)
1598 {
1599 	int	i;
1600 
1601 	for (i = 0; i < num_usr_cols; i++)
1602 	    if (w == colorMemory[i]) {
1603 		pick_memory(i);
1604 		break;
1605 	    }
1606 }
1607 
1608 static void
pick_memory(int which)1609 pick_memory(int which)
1610 {
1611 	/* make sliders sensitive */
1612 	XtSetSensitive(mixingForm, True);
1613 
1614 	modified[edit_fill] = True;
1615 	/* erase box around old memory cell */
1616 	erase_boxed(current_memory);
1617 	/* new memory cell */
1618 	current_memory = which;
1619 	draw_boxed(current_memory);
1620 
1621 	/* put the color number in the mixed index */
1622 	mixed_color_indx[edit_fill] = current_memory+NUM_STD_COLS;
1623 	/* put the color name in the indicator */
1624 	set_mixed_name(edit_fill,mixed_color_indx[edit_fill]);
1625 
1626 	if (!colorFree[current_memory]) {
1627 		do_change = False;
1628 		CHANGE_RED(current_memory);
1629 		CHANGE_GREEN(current_memory);
1630 		CHANGE_BLUE(current_memory);
1631 		do_change = True;
1632 		set_mixed_color(edit_fill);
1633 		update_scrl_triple((Widget)NULL, (XEvent *)NULL,
1634 			(String *)NULL, (Cardinal *)NULL);
1635 	} else {
1636 		user_colors[current_memory].red =
1637 				mixed_color[edit_fill].red;
1638 		user_colors[current_memory].green =
1639 				mixed_color[edit_fill].green;
1640 		user_colors[current_memory].blue =
1641 				mixed_color[edit_fill].blue;
1642 		set_user_color(current_memory);
1643 	}
1644 	/* activate the delete color button */
1645 	XtSetSensitive(delColor, True);
1646 	/* and the hexadecimal window */
1647 	XtSetSensitive(tripleValue[edit_fill], True);
1648 
1649 	/* now set the background of the widget to black if in monochrome */
1650 	if (!all_colors_available) {
1651 	    FirstArg(XtNbackground, x_fg_color.pixel);
1652 	    SetValues(mixedColor[edit_fill]);
1653 	}
1654 }
1655 
1656 static void
lock_toggle(Widget w,XEvent * event,String * params,Cardinal * num_params)1657 lock_toggle(Widget w, XEvent *event, String *params, Cardinal *num_params)
1658 {
1659 	Arg args[1];
1660 	int button = WhichButton(params[0]);
1661 
1662 	args[0].name = XtNbackground;
1663 	if (button & bars_locked) {
1664 		args[0].value = original_background;
1665 		bars_locked -= button;
1666 		if (!bars_locked)
1667 			XtSetSensitive(lockedScroll, False);
1668 	} else {
1669 	    if (!all_colors_available)
1670 		args[0].value = x_fg_color.pixel;
1671 	    else {
1672 		    switch (button) {
1673 			case S_RED:
1674 				args[0].value = colors[RED];
1675 				break;
1676 			case S_GREEN:
1677 				args[0].value = colors[GREEN];
1678 				break;
1679 			case S_BLUE:
1680 				args[0].value = colors[BLUE];
1681 				break;
1682 			default:
1683 				return;
1684 				/* NOT REACHED */
1685 		    }
1686 	    }
1687 	    bars_locked += button;
1688 	    XtSetSensitive(lockedScroll, True);
1689 	}
1690 	move_lock();
1691 	XtSetValues(w, (ArgList) args, 1);
1692 }
1693 
1694 /* change the border for a color button to red and set thickness = 2 */
1695 
1696 static void
draw_boxed(int which)1697 draw_boxed(int which)
1698 {
1699 	if (which < 0)
1700 		return;
1701 	FirstArg(XtNborder, x_color(RED));
1702 	SetValues(colorMemory[which]);
1703 }
1704 
1705 /* change the border for a color button to black if unused or green if used,
1706    and set thickness = 1 */
1707 
1708 static void
erase_boxed(int which)1709 erase_boxed(int which)
1710 {
1711 	if (which < 0)
1712 		return;
1713 	if (colors_used[which]) {
1714 	    FirstArg(XtNborder, x_color(GREEN));
1715 	} else {
1716 	    FirstArg(XtNborder, x_color(BLACK));
1717 	}
1718 	SetValues(colorMemory[which]);
1719 }
1720 
1721 /* color their borders green if in use, black if not */
1722 
color_borders(void)1723 void color_borders(void)
1724 {
1725 	int i;
1726 
1727 	for (i = 0; i < num_usr_cols; i++) {
1728 	    if (colorMemory[i])
1729 		erase_boxed(i);
1730 	}
1731 }
1732 
1733 static int
WhichButton(String name)1734 WhichButton(String name)
1735 {
1736 	if (strcmp(name, "red") == 0)
1737 		return S_RED;
1738 	if (strcmp(name, "blue") == 0)
1739 		return S_BLUE;
1740 	if (strcmp(name, "green") == 0)
1741 		return S_GREEN;
1742 	return 0;
1743 }
1744 
1745 
1746 static void
update_from_triple(Widget w,XEvent * event,String * params,Cardinal * num_params)1747 update_from_triple(Widget w, XEvent *event, String *params, Cardinal *num_params)
1748 {
1749 	char *hexvalue, tmphex[10];
1750 	int red,green,blue;
1751 
1752 	/* get the users hex value from the widget */
1753 	FirstArg(XtNstring, &hexvalue);
1754 	GetValues(tripleValue[edit_fill]);
1755 	if (*hexvalue != '#') {		/* fix it up if user removed the "#" */
1756 	    strcpy(tmphex,"#");
1757 	    strcat(tmphex,hexvalue);
1758 	    hexvalue = tmphex;
1759 	}
1760 	if ((strlen(hexvalue) != 7) ||
1761 	   (sscanf(hexvalue,"#%02x%02x%02x",&red,&green,&blue) != 3)) {
1762 		beep();
1763 		put_msg("Bad hex value");
1764 		return;
1765 	}
1766 	mixed_color[edit_fill].red = red*256;
1767 	mixed_color[edit_fill].green = green*256;
1768 	mixed_color[edit_fill].blue = blue*256;
1769 
1770 	/* and update hsv and rgb scrollbars etc from the new hex value */
1771 	update_scrl_triple(w,event,params,num_params);
1772 
1773 	do_change = False;
1774 	pass_value = 1.0 - rgb_values[edit_fill].r/65536.0;
1775 	Thumbed(redScroll, (XtPointer)S_RED, (XtPointer)(&pass_value));
1776 	pass_value = 1.0 - rgb_values[edit_fill].g/65536.0;
1777 	Thumbed(greenScroll, (XtPointer)S_GREEN, (XtPointer)(&pass_value));
1778 	do_change = True;
1779 	pass_value = 1.0 - rgb_values[edit_fill].b/65536.0;
1780 	Thumbed(blueScroll, (XtPointer)S_BLUE, (XtPointer)(&pass_value));
1781 }
1782 
1783 /* front-end to update_triple called by scrolling in the scrollbars.
1784    call update_triple only if current_memory is not -1 (user color IS selected) */
1785 
1786 static void
update_scrl_triple(Widget w,XEvent * event,String * params,Cardinal * num_params)1787 update_scrl_triple(Widget w, XEvent *event, String *params, Cardinal *num_params)
1788 {
1789 	if (current_memory >= 0)
1790 		update_triple();
1791 }
1792 
1793 static void
update_triple(void)1794 update_triple(void)
1795 {
1796 	char hexvalue[10];
1797 
1798 	(void) sprintf(hexvalue, "#%02x%02x%02x",
1799 			COLOR(edit_fill-2,red),
1800 			COLOR(edit_fill-2,green),
1801 			COLOR(edit_fill-2,blue));
1802 	FirstArg(XtNstring, hexvalue);
1803 	NextArg(XtNinsertPosition, 7/*strlen(hexvalue)*/);
1804 	SetValues(tripleValue[edit_fill]);
1805 
1806 	rgb_values[edit_fill].r = mixed_color[edit_fill].red;
1807 	rgb_values[edit_fill].g = mixed_color[edit_fill].green;
1808 	rgb_values[edit_fill].b = mixed_color[edit_fill].blue;
1809 
1810 	if (!moving_hsv) {
1811 	  hsv_values = RGBToHSV(rgb_values[edit_fill]);
1812 
1813 	  XawScrollbarSetThumb(hueScroll, (float)(1.0 - hsv_values.h), THUMB_H);
1814 	  XawScrollbarSetThumb(satScroll, (float)(1.0 - hsv_values.s), THUMB_H);
1815 	  XawScrollbarSetThumb(valScroll, (float)(1.0 - hsv_values.v), THUMB_H);
1816 	}
1817 }
1818 
1819 static void
move_scroll(Widget w,XEvent * event,String * params,Cardinal * num_params)1820 move_scroll(Widget w, XEvent *event, String *params, Cardinal *num_params)
1821 {
1822 #define ADJUST_CHANGE(color) if (change < 0) { \
1823 					if (color + change < 0) \
1824 						change = -color; \
1825 			     } else { \
1826 					if (color + change > 255) \
1827 						change = 255-color; \
1828 			     }
1829 
1830 	int change;
1831 	float pass_value;
1832 	int red_pos = 0;
1833 	int green_pos = 0;
1834 	int blue_pos = 0;
1835 
1836 	if (buttons_down == 0)
1837 		return;
1838 
1839 	change = last_pos - event->xmotion.y;
1840 	last_pos = event->xmotion.y;
1841 
1842 	if (buttons_down & S_RED) {
1843 		red_pos = mixed_color[edit_fill].red/256;
1844 		ADJUST_CHANGE(red_pos);
1845 	}
1846 
1847 	if (buttons_down & S_GREEN) {
1848 		green_pos = mixed_color[edit_fill].green/256;
1849 		ADJUST_CHANGE(green_pos);
1850 	}
1851 
1852 	if (buttons_down & S_BLUE) {
1853 		blue_pos = mixed_color[edit_fill].blue/256;
1854 		ADJUST_CHANGE(blue_pos);
1855 	}
1856 
1857 	red_pos += change;
1858 	green_pos += change;
1859 	blue_pos += change;
1860 
1861 	/* update the new scroll bar positions and change the color */
1862 	do_change = False;
1863 
1864 	if (buttons_down & S_RED)	{
1865 		pass_value = 1.0 - (float) red_pos/255;
1866 		Thumbed(redScroll, (XtPointer)S_RED, (XtPointer)(&pass_value));
1867 	}
1868 
1869 	if (buttons_down & S_GREEN)	{
1870 		pass_value = 1.0 - (float) green_pos/255;
1871 		Thumbed(greenScroll, (XtPointer)S_GREEN, (XtPointer)(&pass_value));
1872 	}
1873 
1874 	if (buttons_down & S_BLUE)	{
1875 		pass_value = 1.0 - (float) blue_pos/255;
1876 		Thumbed(blueScroll, (XtPointer)S_BLUE, (XtPointer)(&pass_value));
1877 	}
1878 
1879 	do_change = True;
1880 	if (current_memory >= 0) {
1881 	    StoreMix_and_Mem();
1882 	    if (!colorUsed[current_memory])
1883 		colorUsed[current_memory] = True;
1884 	}
1885 	update_scrl_triple((Widget)NULL, (XEvent *)NULL,
1886 			(String *)NULL, (Cardinal *)NULL);
1887 }
1888 
StoreMix_and_Mem(void)1889 void StoreMix_and_Mem(void)
1890 {
1891 	set_mixed_color(edit_fill);
1892 	set_mixed_color(edit_fill);
1893 	user_colors[current_memory].red = mixed_color[edit_fill].red;
1894 	user_colors[current_memory].green = mixed_color[edit_fill].green;
1895 	user_colors[current_memory].blue = mixed_color[edit_fill].blue;
1896 	set_user_color(current_memory);
1897 }
1898 
1899 static void
Scrolled(Widget w,XtPointer closure,XtPointer call_data)1900 Scrolled(Widget w, XtPointer closure, XtPointer call_data)
1901 {
1902 	Boolean going_up = (intptr_t) call_data < 0;
1903 	intptr_t which = (intptr_t) closure;
1904 	int pos = 0;
1905 	float blip = 1.0/256.0;
1906 
1907 	switch (which) {
1908 		case S_RED:
1909 			pos = COLOR(edit_fill-2,red);
1910 			break;
1911 		case S_BLUE:
1912 			pos = COLOR(edit_fill-2,blue);
1913 			break;
1914 		case S_GREEN:
1915 			pos = COLOR(edit_fill-2,green);
1916 			break;
1917 		case S_LOCKED:
1918 			pos = 255 - (int)(locked_top * 255 + 0.5);
1919 			break;
1920 		case S_HUE:
1921 			hsv_values.h += (going_up? blip: -blip);
1922 			ThumbHSV(w, 1.0-hsv_values.h);
1923 			return;
1924 		case S_SAT:
1925 			hsv_values.s += (going_up? blip: -blip);
1926 			ThumbHSV(w, 1.0-hsv_values.s);
1927 			return;
1928 		case S_VAL:
1929 			hsv_values.v += (going_up? blip: -blip);
1930 			ThumbHSV(w, 1.0-hsv_values.v);
1931 			return;
1932 		default:
1933 			fprintf(stderr, "Oops Scroll calldata invalid\n");
1934 			exit(1);
1935 	}
1936 
1937 	if (!going_up) {
1938 		if (pos > 0)
1939 			pos--;
1940 	} else {
1941 		if (pos < 255)
1942 			pos++;
1943 	}
1944 
1945 	pass_value = 1.0 - (float) pos/255.0;
1946 	Thumbed(w, closure, (XtPointer)(&pass_value));
1947 }
1948 
1949 
1950 
1951 static void
Update_HSV(Widget w,XtPointer closure,XtPointer call_data)1952 Update_HSV(Widget w, XtPointer closure, XtPointer call_data)
1953 {
1954 	intptr_t which = (intptr_t) closure;
1955 	float top = *(float*) call_data;
1956 
1957 	switch (which) {
1958 		case S_HUE:
1959 			hsv_values.h = 1.0-top;
1960 			break;
1961 		case S_SAT:
1962 			hsv_values.s = 1.0-top;
1963 			break;
1964 		case S_VAL:
1965 			hsv_values.v = 1.0-top;
1966 			break;
1967 	}
1968 
1969 	ThumbHSV(w, top);
1970 }
1971 
ThumbHSV(Widget w,float top)1972 void ThumbHSV(Widget w, float top)
1973 {
1974 	moving_hsv = True;
1975 
1976 	rgb_values[edit_fill] = HSVToRGB(hsv_values);
1977 	XawScrollbarSetThumb(w, top, THUMB_H);
1978 
1979 	/* don't update the scrollbars yet */
1980 	do_change = False;
1981 	pass_value = 1.0 - rgb_values[edit_fill].r/65536.0;
1982 	Thumbed(redScroll, (XtPointer)S_RED, (XtPointer)(&pass_value));
1983 	pass_value = 1.0 - rgb_values[edit_fill].g/65536.0;
1984 	Thumbed(greenScroll, (XtPointer)S_GREEN, (XtPointer)(&pass_value));
1985 	/* now update the scrollbars */
1986 	do_change = True;
1987 	pass_value = 1.0 - rgb_values[edit_fill].b/65536.0;
1988 	Thumbed(blueScroll, (XtPointer)S_BLUE, (XtPointer)(&pass_value));
1989 
1990 	moving_hsv = False;
1991 }
1992 
1993 
1994 static void
Thumbed(Widget w,XtPointer closure,XtPointer call_data)1995 Thumbed(Widget w, XtPointer closure, XtPointer call_data)
1996 {
1997 	intptr_t which = (intptr_t) closure;
1998 	int mix;
1999 	float top = *(float*) call_data;
2000 	XEvent event;
2001 
2002 	mix = ((int) ((1.0 - top) * 256.0)) << 8;
2003 	if (mix > 0xFFFF)
2004 		mix = 0xFFFF;
2005 
2006 	switch (which) {
2007 		case S_RED:
2008 			mixed_color[edit_fill].red = mix;
2009 			red_top = top;
2010 			break;
2011 		case S_GREEN:
2012 			mixed_color[edit_fill].green = mix;
2013 			green_top = top;
2014 			break;
2015 		case S_BLUE:
2016 			mixed_color[edit_fill].blue = mix;
2017 			blue_top = top;
2018 			break;
2019 		case S_LOCKED:
2020 			buttons_down = bars_locked;
2021 			last_pos = (int) (locked_top*255.0);
2022 			event.xmotion.y = (int)(top*255.0);
2023 			move_scroll(w, &event, (String *)NULL, (Cardinal *)NULL);
2024 			buttons_down = 0;
2025 			return;
2026 	}
2027 	if (do_change) {
2028 	    if (current_memory >= 0) {
2029 		StoreMix_and_Mem();
2030 		if (!colorUsed[current_memory])
2031 			colorUsed[current_memory] = True;
2032 		update_scrl_triple((Widget)NULL, (XEvent *)NULL,
2033 					(String *)NULL, (Cardinal *)NULL);
2034 	    }
2035 	}
2036 	XawScrollbarSetThumb(w, top, THUMB_H);
2037 	move_lock();
2038 }
2039 
2040 
move_lock(void)2041 void move_lock(void)
2042 {
2043 	locked_top = 1.0;
2044 	if (bars_locked & S_RED)
2045 		locked_top = min2(locked_top, red_top);
2046 	if (bars_locked & S_BLUE)
2047 		locked_top = min2(locked_top, blue_top);
2048 	if (bars_locked & S_GREEN)
2049 		locked_top = min2(locked_top, green_top);
2050 	XawScrollbarSetThumb(lockedScroll, locked_top, THUMB_H);
2051 }
2052 
2053 void
next_pencolor(ind_sw_info * sw)2054 next_pencolor(ind_sw_info *sw)
2055 {
2056     while ((++cur_pencolor < NUM_STD_COLS+num_usr_cols) &&
2057 	   (cur_pencolor >= NUM_STD_COLS && colorFree[cur_pencolor-NUM_STD_COLS]))
2058 		;
2059     if (cur_pencolor >= NUM_STD_COLS+num_usr_cols)
2060 	cur_pencolor = DEFAULT;
2061     show_pencolor();
2062 }
2063 
2064 void
prev_pencolor(ind_sw_info * sw)2065 prev_pencolor(ind_sw_info *sw)
2066 {
2067     if (cur_pencolor <= DEFAULT)
2068 	cur_pencolor = NUM_STD_COLS+num_usr_cols;
2069     while ((--cur_pencolor >= NUM_STD_COLS) && colorFree[cur_pencolor-NUM_STD_COLS])
2070 		;
2071     show_pencolor();
2072 }
2073 
2074 /* Update the Pen COLOR in the indicator button */
2075 
2076 void
show_pencolor(void)2077 show_pencolor(void)
2078 {
2079     int		    color;
2080     char	    colorname[10];
2081 
2082     if (cur_pencolor < DEFAULT || cur_pencolor >= NUM_STD_COLS+num_usr_cols ||
2083 	cur_pencolor >= NUM_STD_COLS && colorFree[cur_pencolor-NUM_STD_COLS])
2084 	    cur_pencolor = DEFAULT;
2085     if (cur_pencolor == DEFAULT)
2086 	color = x_fg_color.pixel;
2087     else
2088 	color = all_colors_available ? colors[cur_pencolor] :
2089 			(cur_pencolor == WHITE? x_bg_color.pixel: x_fg_color.pixel);
2090 
2091     recolor_fillstyles();	/* change the colors of the fill style indicators */
2092     /* force re-creation of popup fill style panel next time it is popped up
2093        because the user may have changed to/from black and other color.  Do this
2094        because the tints must be either created or deleted. */
2095     fill_style_sw->panel = (Widget) NULL;
2096     show_fillstyle(fill_style_sw);
2097     if (cur_pencolor < NUM_STD_COLS) {
2098 	strcpy(colorname,colorNames[cur_pencolor + 1].name);
2099 	put_msg("Pen color set to %s", colorNames[cur_pencolor + 1].name);
2100     } else {
2101 	put_msg("Pen color set to user color %d", cur_pencolor);
2102 	sprintf(colorname,"%d",cur_pencolor);
2103     }
2104     /* first erase old colorname */
2105     XDrawImageString(tool_d, pen_color_button->pixmap, ind_button_gc, 3, 25,
2106 	      "        ", 8);
2107     /* now fill the color rectangle with the new color */
2108     XSetForeground(tool_d, pen_color_gc, color);
2109     XFillRectangle(tool_d, pen_color_button->pixmap, pen_color_gc,
2110 			pen_color_button->sw_width - 30, 4, 26, 26);
2111     /* and put the color name in the button also */
2112     XDrawImageString(tool_d, pen_color_button->pixmap, ind_button_gc, 3, 25,
2113 	      colorname, strlen(colorname));
2114     if (pen_color_button->updbut && update_buts_managed)
2115        XtUnmanageChild(pen_color_button->updbut);
2116     FirstArg(XtNbackgroundPixmap, 0);
2117     SetValues(pen_color_button->button);
2118     /* put the pixmap in the widget background */
2119     FirstArg(XtNbackgroundPixmap, pen_color_button->pixmap);
2120     SetValues(pen_color_button->button);
2121     if (pen_color_button->updbut && update_buts_managed)
2122        XtManageChild(pen_color_button->updbut);
2123 }
2124 
2125 void
next_fillcolor(ind_sw_info * sw)2126 next_fillcolor(ind_sw_info *sw)
2127 {
2128     while ((++cur_fillcolor < NUM_STD_COLS+num_usr_cols) &&
2129 	   (cur_fillcolor >= NUM_STD_COLS && colorFree[cur_fillcolor-NUM_STD_COLS]))
2130 		;
2131     if (cur_fillcolor >= NUM_STD_COLS+num_usr_cols)
2132 	cur_fillcolor = DEFAULT;
2133     show_fillcolor();
2134 }
2135 
2136 void
prev_fillcolor(ind_sw_info * sw)2137 prev_fillcolor(ind_sw_info *sw)
2138 {
2139     if (cur_fillcolor <= DEFAULT)
2140 	cur_fillcolor = NUM_STD_COLS+num_usr_cols;
2141     while ((--cur_fillcolor >= NUM_STD_COLS) && colorFree[cur_fillcolor-NUM_STD_COLS])
2142 		;
2143     show_fillcolor();
2144 }
2145 
2146 /* Update the Fill COLOR in the indicator button */
2147 
2148 void
show_fillcolor(void)2149 show_fillcolor(void)
2150 {
2151     int		    color;
2152     char	    colorname[10];
2153 
2154     if (cur_fillcolor < DEFAULT || cur_fillcolor >= NUM_STD_COLS+num_usr_cols ||
2155 	cur_fillcolor >= NUM_STD_COLS && colorFree[cur_fillcolor-NUM_STD_COLS])
2156 	    cur_fillcolor = DEFAULT;
2157     if (cur_fillcolor == DEFAULT)
2158 	color = x_fg_color.pixel;
2159     else
2160 	color = all_colors_available ? colors[cur_fillcolor] :
2161 			(cur_fillcolor == WHITE? x_bg_color.pixel: x_fg_color.pixel);
2162 
2163     recolor_fillstyles();	/* change the colors of the fill style indicators */
2164     /* force re-creation of popup fill style panel next time it is popped up
2165        because the user may have changed to/from black and other color.  Do this
2166        because the tints must be either created or deleted. */
2167     fill_style_sw->panel = (Widget) NULL;
2168     show_fillstyle(fill_style_sw);
2169     if (cur_fillcolor < NUM_STD_COLS) {
2170 	put_msg("Fill color set to %s", colorNames[cur_fillcolor + 1].name);
2171 	strcpy(colorname,colorNames[cur_fillcolor + 1].name);
2172     } else {
2173 	put_msg("Fill color set to user color %d", cur_fillcolor);
2174 	sprintf(colorname,"%d",cur_fillcolor);
2175     }
2176     /* first erase old colorname */
2177     XDrawImageString(tool_d, fill_color_button->pixmap, ind_button_gc, 3, 25,
2178 	      "        ", 8);
2179     /* now fill the color rectangle  with the new fill color */
2180     XSetForeground(tool_d, fill_color_gc, color);
2181     XFillRectangle(tool_d, fill_color_button->pixmap, fill_color_gc,
2182 			fill_color_button->sw_width - 30, 4, 26, 26);
2183     /* and put the color name in the button also */
2184     XDrawImageString(tool_d, fill_color_button->pixmap, ind_button_gc, 3, 25,
2185 	      colorname, strlen(colorname));
2186     if (fill_color_button->updbut && update_buts_managed)
2187        XtUnmanageChild(fill_color_button->updbut);
2188     FirstArg(XtNbackgroundPixmap, 0);
2189     SetValues(fill_color_button->button);
2190     /* put the pixmap in the widget background */
2191     FirstArg(XtNbackgroundPixmap, fill_color_button->pixmap);
2192     SetValues(fill_color_button->button);
2193     if (fill_color_button->updbut && update_buts_managed)
2194        XtManageChild(fill_color_button->updbut);
2195 }
2196 
2197 /* inform the window manager that we have a (possibly) new colormap */
2198 
set_cmap(Window window)2199 void set_cmap(Window window)
2200 {
2201     XSetWindowColormap(tool_d, window, tool_cm);
2202 }
2203 
2204 /*
2205  * color.c - color helper routines
2206  *
2207  * Author:	Christopher A. Kent
2208  *		Western Research Laboratory
2209  *		Digital Equipment Corporation
2210  * Date:	Sun Dec 13 1987
2211  * Copyright (c) 1987 Christopher A. Kent
2212  */
2213 
2214 /*
2215  * See David F. Rogers, "Procedural Elements for Computer Graphics",
2216  * McGraw-Hill, for the theory behind these routines.
2217  */
2218 
2219 /*
2220  * $Log: w_color.c,v $
2221  * Revision 1.1  1995/02/28  15:40:16  feuille
2222  * Initial revision
2223  *
2224  * Revision 1.2  90/06/30  14:32:48  rlh2
2225  * patchlevel 1
2226  *
2227  * Revision 1.1  90/05/10  11:17:30  rlh2
2228  * Initial revision
2229  *
2230  * Revision 1.2  88/06/30  09:58:36  mikey
2231  * Handles CMY also.
2232  *
2233  * Revision 1.1  88/06/30  09:10:32  mikey
2234  * Initial revision
2235  *
2236  */
2237 
2238 #define	MAX_INTENSITY	65535			    /* for X11 */
2239 
2240 #define	ABS(x)	    ((x)<0?-(x):(x))
2241 
2242 RGB	RGBWhite = { MAX_INTENSITY, MAX_INTENSITY, MAX_INTENSITY };
2243 RGB	RGBBlack = { 0, 0, 0 };
2244 
2245 /*
2246  * Convert an HSV to an RGB.
2247  */
2248 
2249 RGB
HSVToRGB(HSV hsv)2250 HSVToRGB(HSV hsv)
2251 {
2252 	RGB	rgb;
2253 	float	p, q, t, f;
2254 	int	i;
2255 
2256 	if (hsv.s == 0.0)
2257 		rgb = PctToRGB(hsv.v, hsv.v, hsv.v);
2258 	else {
2259 		if (hsv.s > 1.0)
2260 			hsv.s = 1.0;
2261 		if (hsv.s < 0.0)
2262 			hsv.s = 0.0;
2263 		if (hsv.v > 1.0)
2264 			hsv.v = 1.0;
2265 		if (hsv.v < 0.0)
2266 			hsv.v = 0.0;
2267 		if (hsv.h >= 1.0)
2268 			hsv.h = 0.0;
2269 
2270 		hsv.h = 6.0 * hsv.h;
2271 		i = (int) hsv.h;
2272 		f = hsv.h - (float) i;
2273 		p = hsv.v * (1.0 - hsv.s);
2274 		q = hsv.v * (1.0 - (hsv.s * f));
2275 		t = hsv.v * (1.0 - (hsv.s * (1.0 - f)));
2276 
2277 		switch(i) {
2278 		case 0:	rgb = PctToRGB(hsv.v, t, p); break;
2279 		case 1:	rgb = PctToRGB(q, hsv.v, p); break;
2280 		case 2:	rgb = PctToRGB(p, hsv.v, t); break;
2281 		case 3:	rgb = PctToRGB(p, q, hsv.v); break;
2282 		case 4:	rgb = PctToRGB(t, p, hsv.v); break;
2283 		case 5:	rgb = PctToRGB(hsv.v, p, q); break;
2284 		}
2285 	}
2286 	return rgb;
2287 }
2288 
2289 /*
2290  * Convert an RGB to HSV.
2291  */
2292 
2293 HSV
RGBToHSV(RGB rgb)2294 RGBToHSV(RGB rgb)
2295 {
2296 	HSV	hsv;
2297 	float	rr, gg, bb;
2298 	float	min, max;
2299 	float	rc, gc, bc;
2300 
2301 	rr = (float) rgb.r / (float) MAX_INTENSITY;
2302 	gg = (float) rgb.g / (float) MAX_INTENSITY;
2303 	bb = (float) rgb.b / (float) MAX_INTENSITY;
2304 
2305 	max = max2(max2(rr, gg), bb);
2306 	min = min2(min2(rr, gg), bb);
2307 	hsv.v = max;
2308 	if (max == 0.0)
2309 		hsv.s = 0.0;
2310 	else
2311 		hsv.s = (max - min) / max;
2312 	if (hsv.s == 0.0)
2313 		hsv.h = 0.0;
2314 	else {
2315 		rc = (max - rr) / (max - min);
2316 		gc = (max - gg) / (max - min);
2317 		bc = (max - bb) / (max - min);
2318 		if (rr == max)
2319 			hsv.h = bc - gc;
2320 		else if (gg == max)
2321 			hsv.h = 2.0 + rc - bc;
2322 		else if (bb == max)
2323 			hsv.h = 4.0 + gc - rc;
2324 
2325 		if (hsv.h < 0.0)
2326 			hsv.h += 6.0;
2327 		hsv.h = hsv.h / 6.0;
2328 	}
2329 	return hsv;
2330 }
2331 
2332 /*
2333  * Intensity percentages to RGB.
2334  */
2335 
2336 RGB
PctToRGB(float rr,float gg,float bb)2337 PctToRGB(float rr, float gg, float bb)
2338 {
2339 	RGB	rgb;
2340 
2341 	if (rr > 1.0)
2342 		rr = 1.0;
2343 	if (gg > 1.0)
2344 		gg = 1.0;
2345 	if (bb > 1.0)
2346 		bb = 1.0;
2347 
2348 	rgb.r = (int)(0.5 + rr * MAX_INTENSITY);
2349 	rgb.g = (int)(0.5 + gg * MAX_INTENSITY);
2350 	rgb.b = (int)(0.5 + bb * MAX_INTENSITY);
2351 	return rgb;
2352 }
2353 
2354 /* +-------------------------------------------------------------------+ */
2355 /* | Copyright 1993, David Koblas (koblas@netcom.com)                  | */
2356 /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
2357 /* |                                                                   | */
2358 /* | Permission to use, copy, modify, and to distribute this software  | */
2359 /* | and its documentation for any purpose is hereby granted without   | */
2360 /* | fee, provided that the above copyright notice appear in all       | */
2361 /* | copies and that both that copyright notice and this permission    | */
2362 /* | notice appear in supporting documentation.  There is no           | */
2363 /* | representations about the suitability of this software for        | */
2364 /* | any purpose.  this software is provided "as is" without express   | */
2365 /* | or implied warranty.                                              | */
2366 /* |                                                                   | */
2367 /* +-------------------------------------------------------------------+ */
2368 
2369 
2370 /*
2371 **  Grab the pixel value from some other window
2372 **
2373 **  Store pixel value in *p and colormap ID in *cmap unless they are NULL.
2374 **
2375 **   General strategy:
2376 **     Grab the cursor
2377 **     Wait for the up/down button event
2378 **     Lookup what window the event is over
2379 **     Query the pixel value
2380  */
2381 static void
DoGrabPixel(Widget w,Pixel * p,Colormap * cmap)2382 DoGrabPixel(Widget w, Pixel *p, Colormap *cmap)
2383 {
2384     int x, y, nx, ny;
2385     XImage *xim;
2386     Colormap amap;
2387     Window root = RootWindowOfScreen(XtScreen(w));
2388     Window window;
2389 
2390     doGrab(w, 0, 0, &x, &y);
2391 
2392     if (cmap == NULL)
2393 	cmap = &amap;
2394 
2395     xyToWindowCmap(tool_d, x, y, root, &nx, &ny, &window, cmap);
2396 
2397     xim = XGetImage(tool_d, window, nx, ny, 1, 1, AllPlanes, ZPixmap);
2398 
2399     if (p != NULL)
2400 	*p = XGetPixel(xim, 0, 0);
2401 
2402     XDestroyImage(xim);
2403 }
2404 
2405 /*
2406  * Grab a rectangle of some window.
2407  * Zero width and height specifies to just grab a single pixel.
2408  * Returns coords of event.
2409  */
2410 
2411 /*
2412 **  Convenience function for doing server pointer grabs
2413  */
2414 #define GRAB_INTERVAL	30
2415 
2416 typedef struct {
2417     XtAppContext app;
2418     Display *dpy;
2419     GC gc;
2420     Window root;
2421     Boolean drawn;
2422     int x, y, ox, oy, width, height;
2423     XtIntervalId id;
2424 } GrabInfo;
2425 
2426 static void
doGrab(Widget w,int width,int height,int * x,int * y)2427 doGrab(Widget w, int width, int height, int *x, int *y)
2428 {
2429     XtAppContext app = XtWidgetToApplicationContext(w);
2430     Window root = DefaultRootWindow(tool_d);
2431     XEvent event;
2432     Cursor cursor = XCreateFontCursor(tool_d, XC_crosshair);
2433     int count = 0;
2434     GrabInfo *info = NULL;
2435 
2436     /* Set up grab cursor */
2437     if (XGrabPointer(tool_d, root, False,
2438 		     info == NULL ? ButtonPressMask | ButtonReleaseMask
2439 	       : ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
2440 		     GrabModeSync, GrabModeAsync,
2441 		     root, cursor, CurrentTime))
2442 	return;
2443 
2444     do {
2445 	XAllowEvents(tool_d, SyncPointer, CurrentTime);
2446 	XtAppNextEvent(app, &event);
2447 	if (event.type == ButtonPress)
2448 	    count++;
2449 	else if (event.type == ButtonRelease) {
2450 	    if (count == 1)
2451 		break;
2452 	    else
2453 		count--;
2454 	} else if (event.type == MotionNotify) {
2455 	    info->x = event.xmotion.x;
2456 	    info->y = event.xmotion.y;
2457 	} else
2458 	    XtDispatchEvent(&event);
2459     }
2460     while (True);
2461 
2462     XUngrabPointer(tool_d, CurrentTime);
2463 
2464     if (info != NULL) {
2465 	/* remove grab cursor */
2466 	if (info->drawn)
2467 	    XDrawRectangle(info->dpy, info->root, info->gc,
2468 			   info->ox - info->width / 2,
2469 			   info->oy - info->height / 2,
2470 			   info->width, info->height);
2471 	XtRemoveTimeOut(info->id);
2472 	XFreeGC(tool_d, info->gc);
2473 	XtFree((XtPointer) info);
2474     }
2475     *x = event.xbutton.x;
2476     *y = event.xbutton.y;
2477 }
2478 
2479 /*
2480  * Given coords x,y in the 'base' window, descend the window hierarchy
2481  * and find the child window of class InputOutput containing those
2482  * coordinates. Return coords in child window in (*nx,*ny).
2483  * If the child window has a colormap, return that; otherwise return
2484  * the default colormap for the display.
2485  */
2486 static void
xyToWindowCmap(Display * dpy,int x,int y,Window base,int * nx,int * ny,Window * window,Colormap * cmap)2487 xyToWindowCmap(Display *dpy, int x, int y, Window base, int *nx, int *ny, Window *window, Colormap *cmap)
2488 {
2489     Window twin;
2490     Colormap tmap;
2491     Window child, sub;
2492     XWindowAttributes attr;
2493 
2494     twin = base;
2495     tmap = None;
2496 
2497     sub = base;
2498     *nx = x;
2499     *ny = y;
2500 
2501     while (sub != None) {
2502 	x = *nx;
2503 	y = *ny;
2504 	child = sub;
2505 	XTranslateCoordinates(dpy, base, child, x, y, nx, ny, &sub);
2506 	base = child;
2507 
2508 	XGetWindowAttributes(dpy, child, &attr);
2509 	if (attr.class == InputOutput && attr.colormap != None) {
2510 	    tmap = attr.colormap;
2511 	    twin = child;
2512 	}
2513     }
2514 
2515     if (tmap == None)
2516 	*cmap = DefaultColormap(dpy, DefaultScreen(dpy));
2517     else
2518 	*cmap = tmap;
2519     *window = twin;
2520 }
2521 
2522