1 #ifndef lint
2 static char sccsid[] = "@(#)xcoloredit.c	1.2 (UKC) 25/1/92";
3 #endif /* !lint */
4 
5 /*
6  * Copyright 1990,1992 Richard Hesketh / rlh2@ukc.ac.uk
7  *                Computing Lab. University of Kent at Canterbury, UK
8  *
9  * Permission to use, copy, modify and distribute this software and its
10  * documentation for any purpose is hereby granted without fee, provided that
11  * the above copyright notice appear in all copies and that both that
12  * copyright notice and this permission notice appear in supporting
13  * documentation, and that the names of Richard Hesketh and The University of
14  * Kent at Canterbury not be used in advertising or publicity pertaining to
15  * distribution of the software without specific, written prior permission.
16  * Richard Hesketh and The University of Kent at Canterbury make no
17  * representations about the suitability of this software for any purpose.
18  * It is provided "as is" without express or implied warranty.
19  *
20  * Richard Hesketh AND THE UNIVERSITY OF KENT AT CANTERBURY DISCLAIMS ALL
21  * WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES
22  * OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Richard Hesketh OR THE
23  * UNIVERSITY OF KENT AT CANTERBURY BE LIABLE FOR ANY SPECIAL, INDIRECT OR
24  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
25  * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
26  * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
27  * OF THIS SOFTWARE.
28  *
29  * Author:  Richard Hesketh / rlh2@ukc.ac.uk,
30  *                Computing Lab. University of Kent at Canterbury, UK
31  */
32 
33 /* A picapix lookalike under X11 */
34 
35 /*
36  * xcoloredit - a colour palette mixer.  Allows existing colormap entries
37  *		to be edited.
38  */
39 
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <X11/Xatom.h>
43 #include <X11/X.h>
44 #include <X11/Intrinsic.h>
45 #include <X11/StringDefs.h>
46 #include <X11/Shell.h>
47 #include <X11/Xaw/Toggle.h>
48 #include <X11/Xaw/Command.h>
49 #include <X11/Xaw/Scrollbar.h>
50 #include <X11/Xaw/Label.h>
51 #include <X11/Xaw/Form.h>
52 #include <X11/Xaw/Box.h>
53 #include <X11/Xmu/Atoms.h>
54 #include <X11/Xmu/StdSel.h>
55 #include "Xcoloredit.h"
56 #include "color.h"
57 
58 #define PRIMARY_COLOR "PRIMARY_COLOR"
59 #define XA_PRIMARY_COLOR XmuInternAtom(XtDisplay(w),_XA_PRIMARY_COLOR)
60 static AtomPtr _XA_PRIMARY_COLOR = NULL;
61 
62 extern void exit();
63 extern void XukcRegisterApplicationDefaults();
64 extern Display *XukcToolkitInitialize();
65 
66 static void OwnSelection();
67 
68 /* callback routines */
69 static void Set_Selection();
70 static void Quit();
71 static void Thumbed();
72 static void Scrolled();
73 static void Load();
74 static void Store();
75 static void Update_HSV();
76 static void Update_Triple();
77 
78 /* action routines */
79 static void lock_toggle();
80 static void pick_memory();
81 static void draw_boxed();
82 static void erase_boxed();
83 static void border();
84 static void update_triple();
85 static void set_scroll();
86 static void stop_scroll();
87 static void move_scroll();
88 static void change_text_colour();
89 
90 static int WhichButton(String name);
91 
92 #define MEMORY_OFFSET	8
93 #define NUM_MEMORIES	36
94 #define NUM_IN_ROW	12
95 #define MAX_COLORS	NUM_MEMORIES+MEMORY_OFFSET
96 #define MIN(a, b) ((a) < (b) ? (a) : (b))
97 #define COLOR(color_el,rgb) (colors[color_el].rgb/256)
98 #define CHANGE_RED(element) \
99 		pass_value = 1.0 - (double)(COLOR(element,red)/255.0); \
100 		Thumbed(redScroll, RED, (XtPointer)(&pass_value))
101 #define CHANGE_GREEN(element) \
102 		pass_value = 1.0 - (double)(COLOR(element,green)/255.0); \
103 		Thumbed(greenScroll, GREEN, (XtPointer)(&pass_value))
104 #define CHANGE_BLUE(element) \
105 		pass_value = 1.0 - (double)(COLOR(element,blue)/255.0); \
106 		Thumbed(blueScroll, BLUE, (XtPointer)(&pass_value))
107 
108 static XColor colors[MAX_COLORS];
109 static Display     *dpy;
110 static Colormap    cmap;
111 static GC	boxedGC, boxedChangedGC, unboxedGC;
112 
113 static RGB rgb_values;
114 static HSV hsv_values;
115 static int buttons_down = 0;
116 static int bars_locked = 0;
117 static double locked_top = 1.0, red_top, green_top, blue_top;
118 static float pass_value;
119 static int last_pos;
120 static Boolean do_change = TRUE;
121 static int current_memory = 0;
122 static Pixel original_background;
123 
124 static Widget toplevel, mixingForm, mixedColor;
125 static Widget redLocked, greenLocked, blueLocked, title;
126 static Widget redIntensity, greenIntensity, blueIntensity, tripleValue;
127 static Widget redScroll, greenScroll, blueScroll, lockedScroll;
128 static Widget hueScroll, satScroll, valScroll;
129 static Widget hueLabel, satLabel, valLabel;
130 static Widget quit;
131 static Widget colorMemory[NUM_MEMORIES];
132 static Boolean colorUsed[NUM_MEMORIES];
133 
134 #define RED    1
135 #define GREEN  2
136 #define BLUE   4
137 #define LOCKED 8
138 #define HUE    16
139 #define SAT    32
140 #define VAL    64
141 
142 #define MIX_COLOR	0
143 #define RED_COLOR	1
144 #define GREEN_COLOR	2
145 #define BLUE_COLOR	3
146 #define PURE_BLACK	4
147 #define PURE_RED	5
148 #define PURE_GREEN	6
149 #define PURE_BLUE	7
150 
151 struct app_resources {
152 	Boolean silent;
153 	String text;
154 	String format;
155 } prog_res;
156 
157 static XtActionsRec actionTable[] = {
158 	{"lock_toggle", lock_toggle},
159 	{"pick_memory", pick_memory},
160 	{"draw_boxed", draw_boxed},
161 	{"erase_boxed", erase_boxed},
162 	{"border", border},
163 	{"update_triple", update_triple},
164 	{"set_scroll", set_scroll},
165 	{"stop_scroll", stop_scroll},
166 	{"move_scroll", move_scroll},
167 	{"change_text_colour", change_text_colour},
168 };
169 
170 static XrmOptionDescRec options[] = {
171 	{ "-silent", ".silent", XrmoptionNoArg, (caddr_t)"true" },
172 	{ "-text", ".text", XrmoptionSepArg, (caddr_t)NULL },
173 	{ "-format", ".format", XrmoptionSepArg, (caddr_t)NULL },
174 };
175 
176 #define offset(field) XtOffset(struct app_resources *, field)
177 static XtResource resources[] = {
178 	{ "silent", "Silent", XtRBoolean, sizeof(Boolean),
179 	  offset(silent), XtRImmediate, (XtPointer)FALSE },
180 	{ "text", "Text", XtRString, sizeof(String),
181 	  offset(text), XtRImmediate, (XtPointer)NULL },
182 	{ "format", "Format", XtRString, sizeof(String),
183 	  offset(format), XtRImmediate, (XtPointer)"#%02x%02x%02x" }
184 };
185 #undef offset
186 
187 
main(argc,argv)188 int main(argc, argv)
189 int argc;
190 char **argv;
191 {
192 	Status ok;
193 	unsigned long plane_masks;
194 	unsigned long pixels[MAX_COLORS];
195 	Cardinal i, j, k;
196 	XGCValues values;
197 	Widget temp;
198 
199 	dpy = XukcToolkitInitialize((String)NULL, "xcoloredit", "Xcoloredit",
200 			&argc, argv,
201 			(XrmOptionDescRec *)options, XtNumber(options));
202 	XukcRegisterApplicationDefaults(DefaultScreenOfDisplay(dpy),
203 					app_defaults, XtNumber(app_defaults));
204         toplevel = XtVaAppCreateShell("xcoloredit", "Xcoloredit",
205                                         applicationShellWidgetClass, dpy,
206                                         NULL);
207 	XtGetApplicationResources(toplevel, &prog_res, resources,
208 					XtNumber(resources), (ArgList)NULL, 0);
209 
210 	cmap = DefaultColormap(dpy, DefaultScreen(dpy));
211 	if(!XAllocColorCells(dpy, cmap, 0, &plane_masks, 0, pixels,
212 	   MAX_COLORS-1))
213 		XtError("xcoloredit: Not enough colormap entries available");
214 
215 	/* allocate the standard cells used by the application */
216 	for (j = 0, i = 1; i < MEMORY_OFFSET; i++) {
217 		colors[i].pixel = pixels[j++];
218 		colors[i].flags = DoRed | DoGreen | DoBlue;
219 	}
220 
221 	/* initialize the pixel memory locations */
222 	for (k = 1, i = MEMORY_OFFSET; i < MAX_COLORS; i++) {
223 		colors[i].flags = DoRed | DoGreen | DoBlue;
224 		if (argc > 1) {
225 			/* pull in any colormap entries specified on the
226 			 * command line */
227 			colors[i].pixel = atoi(argv[k++]);
228 			colorUsed[i-MEMORY_OFFSET] = TRUE;
229 			argc--;
230 		} else {
231 			colors[i].pixel = pixels[j++];
232 			colors[i].red = colors[i].green = colors[i].blue = 0;
233 			colorUsed[i-MEMORY_OFFSET] = FALSE;
234 		}
235 	}
236 
237 	/* retrieve the given colormap entries current values */
238 	if (k > 1)
239 		XQueryColors(dpy, cmap, &(colors[MEMORY_OFFSET]), k-1);
240 
241 	current_memory = 0;
242 
243 	colors[MIX_COLOR].pixel = colors[MEMORY_OFFSET].pixel;
244 	colors[MIX_COLOR].flags = DoRed | DoGreen | DoBlue;
245 	colors[MIX_COLOR].red = colors[MEMORY_OFFSET].red;
246 	colors[MIX_COLOR].green = colors[MEMORY_OFFSET].green;
247 	colors[MIX_COLOR].blue = colors[MEMORY_OFFSET].blue;
248 
249 	colors[PURE_BLACK].red = colors[PURE_BLACK].green = 0;
250 	colors[PURE_BLACK].blue = 0;
251 
252 	colors[PURE_RED].blue = colors[PURE_RED].green = 0;
253 	colors[RED_COLOR].blue = colors[RED_COLOR].green = 0;
254 	colors[PURE_RED].red = colors[RED_COLOR].red = (short)~0;
255 
256 	/* not as pure as it should be!! looks better like this though 8-) */
257 	colors[PURE_GREEN].red = 0x4f*256;
258 	colors[PURE_GREEN].green = 0xb5*256;
259 	colors[PURE_GREEN].blue = 0x1e*256;
260 
261 	colors[GREEN_COLOR].red = colors[GREEN_COLOR].blue = 0;
262 	colors[GREEN_COLOR].green = (short)~0;
263 
264 	colors[PURE_BLUE].red = colors[PURE_BLUE].green = 0;
265 	colors[BLUE_COLOR].red = colors[BLUE_COLOR].green = 0;
266 	colors[PURE_BLUE].blue = colors[BLUE_COLOR].blue = (short)~0;
267 
268 	XStoreColors(dpy, cmap, colors, MAX_COLORS);
269 	for (i = MEMORY_OFFSET; i < j; i++)
270 		colors[i].flags = DoRed | DoGreen | DoBlue;
271 
272 	XtAppAddActions(XtWidgetToApplicationContext(toplevel),
273 					actionTable, XtNumber(actionTable));
274 
275 	/* create the UI */
276 	mixingForm = XtVaCreateManagedWidget("mixingForm", formWidgetClass,
277 						toplevel, NULL);
278 	redLocked = XtVaCreateManagedWidget("redLocked", labelWidgetClass,
279 						mixingForm, NULL);
280 	greenLocked = XtVaCreateManagedWidget("greenLocked", labelWidgetClass,
281 						mixingForm, NULL);
282 	blueLocked = XtVaCreateManagedWidget("blueLocked", labelWidgetClass,
283 						mixingForm, NULL);
284 	title = XtVaCreateManagedWidget("title", labelWidgetClass,
285 						mixingForm, NULL);
286 	redScroll = XtVaCreateManagedWidget("redScroll", scrollbarWidgetClass,
287 						mixingForm, NULL);
288 	greenScroll = XtVaCreateManagedWidget("greenScroll",
289 				scrollbarWidgetClass, mixingForm, NULL);
290 	blueScroll = XtVaCreateManagedWidget("blueScroll", scrollbarWidgetClass,
291 						mixingForm, NULL);
292 	lockedScroll = XtVaCreateManagedWidget("lockedScroll",
293 				scrollbarWidgetClass, mixingForm, NULL);
294 	mixedColor = XtVaCreateManagedWidget("mixedColor", labelWidgetClass,
295 					mixingForm,
296 					XtVaTypedArg, XtNforeground,
297 					XtRString, "white", 6,
298 					XtNbackground, colors[MIX_COLOR].pixel,
299 					NULL);
300 	if (prog_res.text != NULL)
301 		XtVaSetValues(mixedColor, XtNlabel, prog_res.text, NULL);
302 	redIntensity = XtVaCreateManagedWidget("redIntensity", widgetClass,
303 					mixingForm,
304 					XtNbackground, colors[RED_COLOR].pixel,
305 					NULL);
306 	greenIntensity = XtVaCreateManagedWidget("greenIntensity", widgetClass,
307 				mixingForm,
308 				XtNbackground, colors[GREEN_COLOR].pixel,
309 				NULL);
310 	blueIntensity = XtVaCreateManagedWidget("blueIntensity", widgetClass,
311 				mixingForm,
312 				XtNbackground, colors[BLUE_COLOR].pixel,
313 				NULL);
314 	tripleValue = XtVaCreateManagedWidget("tripleValue", toggleWidgetClass,
315 						mixingForm, NULL);
316 	quit = XtVaCreateManagedWidget("quit", commandWidgetClass, mixingForm,
317 						NULL);
318 	hueLabel = XtVaCreateManagedWidget("hueLabel", labelWidgetClass,
319 						mixingForm, NULL);
320 	satLabel = XtVaCreateManagedWidget("satLabel", labelWidgetClass,
321 						mixingForm, NULL);
322 	valLabel = XtVaCreateManagedWidget("valLabel", labelWidgetClass,
323 						mixingForm, NULL);
324 	hueScroll = XtVaCreateManagedWidget("hueScroll", scrollbarWidgetClass,
325 						mixingForm, NULL);
326 	satScroll = XtVaCreateManagedWidget("satScroll", scrollbarWidgetClass,
327 						mixingForm, NULL);
328 	valScroll = XtVaCreateManagedWidget("valScroll", scrollbarWidgetClass,
329 						mixingForm, NULL);
330 
331 	temp = quit;
332 	for (i = 0; i < NUM_MEMORIES; i++) {
333 		if (i > 0 && i%NUM_IN_ROW == 0)
334 			temp = colorMemory[i-1];
335 		colorMemory[i] = XtVaCreateManagedWidget("colorMemory",
336 				widgetClass, mixingForm,
337 				XtNbackground, colors[i+MEMORY_OFFSET].pixel,
338 				XtNfromVert, temp,
339 				XtNfromHoriz,
340 				(i%NUM_IN_ROW ? colorMemory[i-1] : NULL),
341 				NULL);
342 	}
343 
344 	values.foreground = colors[PURE_RED].pixel;
345 	values.line_width = 2;
346 	values.line_style = LineOnOffDash;
347 	boxedChangedGC = XtGetGC(mixingForm, GCForeground | GCLineWidth,
348 					&values);
349 	boxedGC = XtGetGC(mixingForm, GCForeground | GCLineWidth | GCLineStyle,
350 					&values);
351 
352 	XtVaGetValues(mixingForm, XtNbackground, &(values.foreground), NULL);
353 	unboxedGC = XtGetGC(mixingForm, GCForeground | GCLineWidth, &values);
354 
355 	original_background = values.foreground;
356 	bars_locked = NULL;
357 
358 	XtAddCallback(redScroll, XtNjumpProc, Thumbed, (XtPointer)RED);
359 	XtAddCallback(greenScroll, XtNjumpProc, Thumbed, (XtPointer)GREEN);
360 	XtAddCallback(blueScroll, XtNjumpProc, Thumbed, (XtPointer)BLUE);
361 	XtAddCallback(lockedScroll, XtNjumpProc, Thumbed, (XtPointer)LOCKED);
362 
363 	XtAddCallback(redScroll, XtNscrollProc, Scrolled, (XtPointer)RED);
364 	XtAddCallback(greenScroll, XtNscrollProc, Scrolled, (XtPointer)GREEN);
365 	XtAddCallback(blueScroll, XtNscrollProc, Scrolled, (XtPointer)BLUE);
366 	XtAddCallback(lockedScroll, XtNscrollProc, Scrolled, (XtPointer)LOCKED);
367 	XtAddCallback(hueScroll, XtNscrollProc, Scrolled, (XtPointer)HUE);
368 	XtAddCallback(satScroll, XtNscrollProc, Scrolled, (XtPointer)SAT);
369 	XtAddCallback(valScroll, XtNscrollProc, Scrolled, (XtPointer)VAL);
370 
371 	XtAddCallback(hueScroll, XtNjumpProc, Update_HSV, (XtPointer)HUE);
372 	XtAddCallback(satScroll, XtNjumpProc, Update_HSV, (XtPointer)SAT);
373 	XtAddCallback(valScroll, XtNjumpProc, Update_HSV, (XtPointer)VAL);
374 
375 	XtAddCallback(tripleValue, XtNcallback, Set_Selection,
376 						(XtPointer)PRIMARY_COLOR);
377 	XtAddCallback(quit, XtNcallback, Quit, (XtPointer)toplevel);
378 
379 	XtRealizeWidget(toplevel);
380 
381 	/* set the scrollbars to the initial mixed colour values */
382 	do_change = FALSE;
383 	CHANGE_RED(MIX_COLOR);
384 	CHANGE_GREEN(MIX_COLOR);
385 	CHANGE_BLUE(MIX_COLOR);
386 	do_change = TRUE;
387 	XStoreColors(dpy, cmap, colors, 4);
388 	update_triple((Widget)NULL, (XEvent *)NULL,
389 					(String *)NULL, (Cardinal *)NULL);
390 
391 	XtAppMainLoop(XtWidgetToApplicationContext(toplevel));
392 }
393 
394 /* ARGSUSED */
395 static void
Quit(w,client,call)396 Quit(w, client, call)
397 Widget w;
398 XtPointer client, call;
399 {
400 	Cardinal i;
401 
402 	colors[current_memory+MEMORY_OFFSET].red = colors[MIX_COLOR].red;
403 	colors[current_memory+MEMORY_OFFSET].green = colors[MIX_COLOR].green;
404 	colors[current_memory+MEMORY_OFFSET].blue = colors[MIX_COLOR].blue;
405 
406 	if (!prog_res.silent)
407 		for (i = 0; i < NUM_MEMORIES; i++)
408 			if (colorUsed[i]) {
409 				printf(prog_res.format,
410 					  COLOR(i+MEMORY_OFFSET,red),
411 					  COLOR(i+MEMORY_OFFSET,green),
412 					  COLOR(i+MEMORY_OFFSET,blue));
413 				putchar('\n');
414 			}
415 	XtReleaseGC(mixingForm, boxedGC);
416 	XtReleaseGC(mixingForm, boxedChangedGC);
417 	XtReleaseGC(mixingForm, unboxedGC);
418 	exit(0);
419 }
420 
421 static void
lock_toggle(w,event,params,num_params)422 lock_toggle(w, event, params, num_params)
423 Widget w;
424 XEvent *event;
425 String *params;
426 Cardinal *num_params;
427 {
428 	Arg args[1];
429 	int button = WhichButton(params[0]);
430 
431 	args[0].name = XtNbackground;
432 	if (button & bars_locked) {
433 		args[0].value = original_background;
434 		bars_locked -= button;
435 		if (!bars_locked)
436 			XtSetSensitive(lockedScroll, FALSE);
437 	} else {
438 		switch (button) {
439 			case RED:
440 				args[0].value = colors[PURE_RED].pixel;
441 				break;
442 			case GREEN:
443 				args[0].value = colors[PURE_GREEN].pixel;
444 				break;
445 			case BLUE:
446 				args[0].value = colors[PURE_BLUE].pixel;
447 				break;
448 			default:
449 				return;
450 				/* NOT REACHED */
451 		}
452 		bars_locked += button;
453 		XtSetSensitive(lockedScroll, TRUE);
454 	}
455 	move_lock();
456 	XtSetValues(w, (ArgList)args, 1);
457 }
458 
459 
460 /* draw a dashed box around the given widget */
461 static void
draw_a_dotted_box(w,gc)462 draw_a_dotted_box(w, gc)
463 Widget w;
464 GC gc;
465 {
466 	Position x, y;
467 	Dimension width, height, border_width;
468 	Arg args[5];
469 
470 	XtSetArg(args[0], XtNborderWidth, &border_width);
471 	XtSetArg(args[1], XtNx, &x);
472 	XtSetArg(args[2], XtNy, &y);
473 	XtSetArg(args[3], XtNwidth, &width);
474 	XtSetArg(args[4], XtNheight, &height);
475 	XtGetValues(w, (ArgList)args, 5);
476 
477 	x -= border_width + 1;
478 	y -= border_width + 1;
479 	width += border_width*2 + 4;
480 	height += border_width*2 + 4;
481 
482 	XDrawRectangle(XtDisplay(mixingForm), XtWindow(mixingForm), gc,
483 							x, y, width, height);
484 }
485 
486 
487 static void
draw_boxed(w,event,params,num_params)488 draw_boxed(w, event, params, num_params)
489 Widget w;
490 XEvent *event;
491 String *params;
492 Cardinal *num_params;
493 {
494 	draw_a_dotted_box(colorMemory[current_memory],
495 				colorUsed[current_memory] ? boxedChangedGC :
496 				boxedGC);
497 }
498 
499 
500 static void
erase_boxed(w,event,params,num_params)501 erase_boxed(w, event, params, num_params)
502 Widget w;
503 XEvent *event;
504 String *params;
505 Cardinal *num_params;
506 {
507 	draw_a_dotted_box(colorMemory[current_memory], unboxedGC);
508 }
509 
510 
511 static void
pick_memory(w,event,params,num_params)512 pick_memory(w, event, params, num_params)
513 Widget w;
514 XEvent *event;
515 String *params;
516 Cardinal *num_params;
517 {
518 	int i;
519 
520 	for (i = 0; i < NUM_MEMORIES; i++) {
521 		if (w == colorMemory[i]) {
522 			/* old memory cell */
523 			draw_a_dotted_box(colorMemory[current_memory],
524 					  unboxedGC);
525 			colors[current_memory+MEMORY_OFFSET].red =
526 						colors[MIX_COLOR].red;
527 			colors[current_memory+MEMORY_OFFSET].green =
528 						colors[MIX_COLOR].green;
529 			colors[current_memory+MEMORY_OFFSET].blue =
530 						colors[MIX_COLOR].blue;
531 			XStoreColor(dpy, cmap,
532 				&(colors[current_memory+MEMORY_OFFSET]));
533 
534 			/* new memory cell */
535 			current_memory = i;
536 			draw_boxed((Widget)NULL, (XEvent *)NULL,
537 					(String *)NULL, (Cardinal *)NULL);
538 
539 			colors[MIX_COLOR].pixel =
540 				colors[current_memory+MEMORY_OFFSET].pixel;
541 
542 			if (colorUsed[current_memory]) {
543 				do_change = FALSE;
544 				CHANGE_RED(current_memory+MEMORY_OFFSET);
545 				CHANGE_GREEN(current_memory+MEMORY_OFFSET);
546 				CHANGE_BLUE(current_memory+MEMORY_OFFSET);
547 				do_change = TRUE;
548 				XStoreColors(dpy, cmap, colors, 4);
549 				update_triple((Widget)NULL, (XEvent *)NULL,
550 					(String *)NULL, (Cardinal *)NULL);
551 			} else {
552 				colors[current_memory+MEMORY_OFFSET].red =
553 						colors[MIX_COLOR].red;
554 				colors[current_memory+MEMORY_OFFSET].green =
555 						colors[MIX_COLOR].green;
556 				colors[current_memory+MEMORY_OFFSET].blue =
557 						colors[MIX_COLOR].blue;
558 				XStoreColor(dpy, cmap,
559 				      &(colors[current_memory+MEMORY_OFFSET]));
560 			}
561 			XtVaSetValues(mixedColor, XtNbackground,
562 					colors[MIX_COLOR].pixel, NULL);
563 			break;
564 		}
565 	}
566 }
567 
568 static void
ChangeBorder(button,now_up)569 ChangeBorder(button, now_up)
570 int button;
571 Boolean now_up;
572 {
573 	Widget scrollbar;
574 
575 	switch (button) {
576 		case RED:
577 			scrollbar = redScroll;
578 			button = now_up ? PURE_RED : PURE_BLACK;
579 			break;
580 		case GREEN:
581 			scrollbar = greenScroll;
582 			button = now_up ? PURE_GREEN : PURE_BLACK;
583 			break;
584 		case BLUE:
585 			scrollbar = blueScroll;
586 			button = now_up ? PURE_BLUE : PURE_BLACK;
587 			break;
588 		default:
589 			return;
590 			/* NOTREACHED */
591 	}
592 	XtVaSetValues(scrollbar, XtNborderColor, colors[button].pixel, NULL);
593 }
594 
595 static void
border(w,event,params,num_params)596 border(w, event, params, num_params)
597 Widget w;
598 XEvent *event;
599 String *params;
600 Cardinal *num_params;
601 {
602 	Boolean reset = (Boolean)(*num_params == 1 && strcmp(params[0], "reset") == 0);
603 
604 	if (w == redScroll)
605 		ChangeBorder(RED, reset);
606 	else {
607 		if (w == greenScroll)
608 			ChangeBorder(GREEN, reset);
609 		else {
610 			if (w == blueScroll)
611 				ChangeBorder(BLUE, reset);
612 			else {
613 				if (bars_locked & RED) ChangeBorder(RED, reset);
614 				if (bars_locked & GREEN) ChangeBorder(GREEN, reset);
615 				if (bars_locked & BLUE) ChangeBorder(BLUE, reset);
616 			}
617 		}
618 	}
619 }
620 
621 
622 static int
WhichButton(name)623 WhichButton(name)
624 String name;
625 {
626 	if (strcmp(name, "red") == 0)
627 		return RED;
628 	if (strcmp(name, "blue") == 0)
629 		return BLUE;
630 	if (strcmp(name, "green") == 0)
631 		return GREEN;
632 	return 0;
633 }
634 
635 
636 static void
Update_Triple(w,client_data,call_data)637 Update_Triple(w, client_data, call_data)
638 Widget w;
639 XtPointer client_data, call_data;
640 {
641 	String old_str;
642 	char old_value[10];
643 	Cardinal r, g, b;
644 
645 	XtVaGetValues(w, XtNlabel, &old_str, NULL);
646 	(void)strncpy(old_str, old_value, 8);
647 	if (sscanf((char *)call_data, prog_res.format, &r, &g, &b) != 3) {
648 		XtVaSetValues(w, XtNlabel, old_value, NULL);
649 	}
650 }
651 
652 
653 static void
change_text_colour(w,event,params,num_params)654 change_text_colour(w, event, params, num_params)
655 Widget w;
656 XEvent *event;
657 String *params;
658 Cardinal *num_params;
659 {
660 	XtVaSetValues(mixedColor, XtNforeground,
661 			colors[current_memory+MEMORY_OFFSET].pixel,
662 			NULL);
663 }
664 
665 
666 static void
update_triple(w,event,params,num_params)667 update_triple(w, event, params, num_params)
668 Widget w;
669 XEvent *event;
670 String *params;
671 Cardinal *num_params;
672 {
673 	Arg args[1];
674 	char hexvalue[10];
675 
676 	(void)sprintf(hexvalue, prog_res.format,
677 			COLOR(MIX_COLOR,red), COLOR(MIX_COLOR,green),
678 			COLOR(MIX_COLOR,blue));
679 	XtVaSetValues(tripleValue, XtNlabel, hexvalue, NULL);
680 	OwnSelection(tripleValue, (XtPointer)FALSE, (XtPointer)TRUE);
681 
682 	rgb_values.r = colors[MIX_COLOR].red;
683 	rgb_values.g = colors[MIX_COLOR].green;
684 	rgb_values.b = colors[MIX_COLOR].blue;
685 
686 	hsv_values = RGBToHSV(rgb_values);
687 
688 #ifdef SOLID_THUMB
689 	XawScrollbarSetThumb(hueScroll, (float)(1.0 - hsv_values.h),
690 								hsv_values.h);
691 	XawScrollbarSetThumb(satScroll, (float)(1.0 - hsv_values.s),
692 								hsv_values.s);
693 	XawScrollbarSetThumb(valScroll, (float)(1.0 - hsv_values.v),
694 								hsv_values.v);
695 #else
696 	XawScrollbarSetThumb(hueScroll, (float)(1.0 - hsv_values.h),
697 								(float)0.025);
698 	XawScrollbarSetThumb(satScroll, (float)(1.0 - hsv_values.s),
699 								(float)0.025);
700 	XawScrollbarSetThumb(valScroll, (float)(1.0 - hsv_values.v),
701 								(float)0.025);
702 #endif /* SOLID_THUMB */
703 }
704 
705 
706 static void
set_scroll(w,event,params,num_params)707 set_scroll(w, event, params, num_params)
708 Widget w;
709 XEvent *event;
710 String *params;
711 Cardinal *num_params;
712 {
713 	int button_down = WhichButton(params[0]);
714 	last_pos = event->xbutton.y;
715 	buttons_down |= button_down;
716 	ChangeBorder(button_down, FALSE);
717 }
718 
719 
720 static void
stop_scroll(w,event,params,num_params)721 stop_scroll(w, event, params, num_params)
722 Widget w;
723 XEvent *event;
724 String *params;
725 Cardinal *num_params;
726 {
727 	int button_up = WhichButton(params[0]);
728 	buttons_down &= ~button_up;
729 	ChangeBorder(button_up, TRUE);
730 	if (!buttons_down)
731 		update_triple(w, event, params, num_params);
732 }
733 
734 
735 static void
move_scroll(w,event,params,num_params)736 move_scroll(w, event, params, num_params)
737 Widget w;
738 XEvent *event;
739 String *params;
740 Cardinal *num_params;
741 {
742 #define ADJUST_CHANGE(color) if (change < 0) { \
743 					if (color + change < 0) \
744 						change = -color; \
745 			     } else { \
746 					if (color + change > 255) \
747 						change = 255-color; \
748 			     }
749 
750 	int change;
751 	float pass_value;
752 	int red_pos, green_pos, blue_pos;
753 
754 	if (buttons_down == 0)
755 		return;
756 
757 	change = last_pos - event->xmotion.y;
758 	last_pos = event->xmotion.y;
759 
760 	if (buttons_down & RED) {
761 		red_pos = colors[MIX_COLOR].red/256;
762 		ADJUST_CHANGE(red_pos);
763 	}
764 
765 	if (buttons_down & GREEN) {
766 		green_pos = colors[MIX_COLOR].green/256;
767 		ADJUST_CHANGE(green_pos);
768 	}
769 
770 	if (buttons_down & BLUE) {
771 		blue_pos = colors[MIX_COLOR].blue/256;
772 		ADJUST_CHANGE(blue_pos);
773 	}
774 
775 	red_pos += change;
776 	green_pos += change;
777 	blue_pos += change;
778 
779 	/* update the new scroll bar positions and change the color */
780 	do_change = FALSE;
781 
782 	if (buttons_down & RED)	{
783 		pass_value = 1.0 - (float)red_pos/255;
784 		Thumbed(redScroll, RED, (XtPointer)(&pass_value));
785 	}
786 
787 	if (buttons_down & GREEN)	{
788 		pass_value = 1.0 - (float)green_pos/255;
789 		Thumbed(greenScroll, GREEN, (XtPointer)(&pass_value));
790 	}
791 
792 	if (buttons_down & BLUE)	{
793 		pass_value = 1.0 - (float)blue_pos/255;
794 		Thumbed(blueScroll, BLUE, (XtPointer)(&pass_value));
795 	}
796 
797 	do_change = TRUE;
798 	XStoreColors(dpy, cmap, colors, 4);
799 	if (!colorUsed[current_memory]) {
800 		colorUsed[current_memory] = TRUE;
801 		draw_boxed((Widget)NULL, (XEvent *)NULL,
802 				(String *)NULL, (Cardinal *)NULL);
803 	}
804 	update_triple((Widget)NULL, (XEvent *)NULL,
805 			(String *)NULL, (Cardinal *)NULL);
806 }
807 
808 
809 static void
Scrolled(w,closure,change)810 Scrolled(w, closure, change)
811 Widget w;
812 XtPointer closure, change;
813 {
814 	Boolean going_up = (int)change < 0;
815 	int which = (int)closure;
816 	int pos = 0;
817 
818 	switch (which) {
819 		case RED:
820 			pos = COLOR(MIX_COLOR,red);
821 			break;
822 		case BLUE:
823 			pos = COLOR(MIX_COLOR,blue);
824 			break;
825 		case GREEN:
826 			pos = COLOR(MIX_COLOR,green);
827 			break;
828 		case LOCKED:
829 			pos = 255 - (int)(locked_top * 255 + 0.5);
830 			break;
831 		case HUE:
832 		case SAT:
833 		case VAL:
834 			/* Not yet implemented */
835 			return;
836 		default:
837 			fprintf(stderr, "Oops Scroll calldata invalid\n");
838 			exit(1);
839 	}
840 
841 	if (going_up) {
842 		if (pos > 0)
843 			pos--;
844 	} else {
845 		if (pos < 255)
846 			pos++;
847 	}
848 
849 	pass_value = 1.0 - (double)pos/255;
850 	Thumbed(w, closure, (XtPointer)(&pass_value));
851 }
852 
853 
854 
855 static void
Update_HSV(w,closure,ptr)856 Update_HSV(w, closure, ptr)
857 Widget w;
858 XtPointer closure, ptr;
859 {
860 	int which = (int)closure;
861 	float r, g, b;
862 	float per = *(float*)ptr;
863 	double top = (double)per;
864 	XEvent event;
865 
866 	switch (which) {
867 		case HUE:
868 			hsv_values.h = 1.0 - top;
869 			break;
870 		case SAT:
871 			hsv_values.s = 1.0 - top;
872 			break;
873 		case VAL:
874 			hsv_values.v = 1.0 - top;
875 			break;
876 	}
877 
878 	rgb_values = HSVToRGB(hsv_values);
879 
880 #ifdef SOLID_THUMB
881 	XawScrollbarSetThumb(w, top, (float)(1.0 - top));
882 #else
883 	XawScrollbarSetThumb(w, top, (float)0.025);
884 #endif /* SOLID_THUMB */
885 
886 	do_change = FALSE;
887 	pass_value = 1.0 - rgb_values.r/65536.0;
888 	Thumbed(redScroll, (XtPointer)RED, (XtPointer)(&pass_value));
889 	pass_value = 1.0 - rgb_values.g/65536.0;
890 	Thumbed(greenScroll, (XtPointer)GREEN, (XtPointer)(&pass_value));
891 	do_change = TRUE;
892 	pass_value = 1.0 - rgb_values.b/65536.0;
893 	Thumbed(blueScroll, (XtPointer)BLUE, (XtPointer)(&pass_value));
894 }
895 
896 
897 static void
Thumbed(w,closure,ptr)898 Thumbed(w, closure, ptr)
899 Widget w;
900 XtPointer closure, ptr;
901 {
902 	int which = (int)closure;
903 	int mix;
904 	float per = *(float*)ptr;
905 	double top = (double)per;
906 	XEvent event;
907 
908 	mix = (int) ((1.0 - top) * 256.0 * 256.0);
909 	if (mix > 0xFFFF)
910 		mix = 0xFFFF;
911 
912 	switch (which) {
913 		case RED:
914 			colors[MIX_COLOR].red = colors[RED_COLOR].red = mix;
915 			red_top = top;
916 			break;
917 		case GREEN:
918 			colors[MIX_COLOR].green = colors[GREEN_COLOR].green = mix;
919 			green_top = top;
920 			break;
921 		case BLUE:
922 			colors[MIX_COLOR].blue = colors[BLUE_COLOR].blue = mix;
923 			blue_top = top;
924 			break;
925 		case LOCKED:
926 			buttons_down = bars_locked;
927 			last_pos = (int)((double)locked_top*255);
928 			event.xmotion.y = (int)((double)top*255);
929 			move_scroll(w, &event, (String *)NULL, (Cardinal *)NULL);
930 			buttons_down = 0;
931 			return;
932 	}
933 	if (do_change) {
934 		XStoreColors(dpy, cmap, colors, 4);
935 		if (!colorUsed[current_memory]) {
936 			colorUsed[current_memory] = TRUE;
937 			draw_boxed((Widget)NULL, (XEvent *)NULL,
938 					(String *)NULL, (Cardinal *)NULL);
939 		}
940 		update_triple((Widget)NULL, (XEvent *)NULL,
941 					(String *)NULL, (Cardinal *)NULL);
942 	}
943 #ifdef SOLID_THUMB
944 	XawScrollbarSetThumb(w, top, (float)(1.0 - top));
945 #else
946 	XawScrollbarSetThumb(w, top, (float)0.025);
947 #endif /* SOLID_THUMB */
948 	move_lock();
949 }
950 
951 
move_lock()952 move_lock()
953 {
954 	locked_top = 1.0;
955 	if (bars_locked & RED)
956 		locked_top = MIN(locked_top, red_top);
957 	if (bars_locked & BLUE)
958 		locked_top = MIN(locked_top, blue_top);
959 	if (bars_locked & GREEN)
960 		locked_top = MIN(locked_top, green_top);
961 #ifdef SOLID_THUMB
962 	XawScrollbarSetThumb(lockedScroll, locked_top,
963 						(float)(1.0 - locked_top));
964 #else
965 	XawScrollbarSetThumb(lockedScroll, locked_top, (float)0.025);
966 #endif /* SOLID_THUMB */
967 }
968 
969 
970 /* Set the current selection of the PRIMARY_COLOR property to this colour */
971 /* ARGSUSED */
972 static void
Set_Selection(w,client_data,call_data)973 Set_Selection(w, client_data, call_data)
974 Widget w;
975 XtPointer client_data, call_data;
976 {
977 	Boolean own;
978 
979 	XtVaGetValues(w, XtNstate, &own, NULL);
980 	OwnSelection(w, (XtPointer)TRUE, (XtPointer)(own ? TRUE : FALSE));
981 }
982 
983 
984 static Boolean
ConvertSelection(w,selection,target,type,value,length,format)985 ConvertSelection(w, selection, target, type, value, length, format)
986 Widget w;
987 Atom *selection, *target, *type;
988 XtPointer *value;
989 unsigned long *length;
990 int *format;
991 {
992 	if (XmuConvertStandardSelection(w, (Time)0, selection, target, type,
993 	    (XPointer *)value, length, format))
994 		return TRUE;
995 
996 	if (*target == XA_STRING) {
997 		String color_str;
998 
999 		XtVaGetValues(tripleValue, XtNlabel, &color_str, NULL);
1000 		*type = XA_STRING;
1001 		*value = color_str;
1002 		*length = strlen(*value);
1003 		*format = 8;
1004 		return (TRUE);
1005 	}
1006 	return (FALSE);
1007 }
1008 
1009 
1010 /* ARGSUSED */
1011 static void
LoseSelection(w,selection)1012 LoseSelection(w, selection)
1013 Widget w;
1014 Atom *selection;
1015 {
1016 	XtVaSetValues(w, XtNstate, FALSE, NULL);
1017 }
1018 
1019 
1020 /* ARGSUSED */
1021 static void
DoneSelection(w,selection,target)1022 DoneSelection(w, selection, target)
1023 Widget w;
1024 Atom *selection, *target;
1025 {
1026 	/* we don't need to do anything here */
1027 }
1028 
1029 
1030 /* ARGSUSED */
1031 static void
OwnSelection(w,client_data,call_data)1032 OwnSelection(w, client_data, call_data)
1033 Widget w;
1034 XtPointer client_data, call_data;
1035 {
1036 	Time time = XtLastTimestampProcessed(XtDisplay(w));
1037 	Boolean primary = (Boolean)client_data;
1038 	Boolean own = (Boolean)call_data;
1039 
1040 	if (_XA_PRIMARY_COLOR == NULL)
1041 		_XA_PRIMARY_COLOR = XmuMakeAtom(PRIMARY_COLOR);
1042 
1043 	if (own) {
1044 		XtOwnSelection(w, XA_PRIMARY_COLOR, time,
1045 			ConvertSelection, LoseSelection, DoneSelection);
1046 		if (primary)
1047 			XtOwnSelection(w, XA_PRIMARY, time,
1048 			   ConvertSelection, LoseSelection, DoneSelection);
1049 	} else {
1050 		XtDisownSelection(w, XA_PRIMARY_COLOR, time);
1051 		if (primary)
1052 		    XtDisownSelection(w, XA_PRIMARY, time);
1053 	}
1054 }
1055