1 /* widget.c -- Xlib widget support for xspringies
2  * Copyright (C) 1991,1992  Douglas M. DeCarlo
3  *
4  * This file is part of XSpringies, a mass and spring simulation system for X
5  *
6  * XSpringies is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 1, or (at your option)
9  * any later version.
10  *
11  * XSpringies is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with XSpringies; see the file COPYING.  If not, write to
18  * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21 
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include "defs.h"
25 
26 #define NAME_LEN	32
27 #define MAX_OBJS	16
28 #define NUM_DIGS        12
29 
30 /* Bitmaps used */
31 static unsigned char check_bits[] = {
32    0x00, 0x00, 0xfe, 0x7f, 0x06, 0x60, 0x0a, 0x50, 0x12, 0x48, 0x22, 0x44,
33    0x42, 0x42, 0x82, 0x41, 0x82, 0x41, 0x42, 0x42, 0x22, 0x44, 0x12, 0x48,
34    0x0a, 0x50, 0x06, 0x60, 0xfe, 0x7f, 0x00, 0x00};
35 static unsigned char checked_bits[] = {
36    0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x0e, 0x70, 0x16, 0x68, 0x26, 0x64,
37    0x46, 0x62, 0x86, 0x61, 0x86, 0x61, 0x46, 0x62, 0x26, 0x64, 0x16, 0x68,
38    0x0e, 0x70, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00};
39 static unsigned char unchecked_bits[] = {
40    0x00, 0x00, 0xfe, 0x7f, 0xfe, 0x7f, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60,
41    0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60, 0x06, 0x60,
42    0x06, 0x60, 0xfe, 0x7f, 0xfe, 0x7f, 0x00, 0x00};
43 static unsigned char box_bits[] = {
44    0x00, 0x00, 0xfe, 0x7f, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
45    0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40, 0x02, 0x40,
46    0x02, 0x40, 0x02, 0x40, 0xfe, 0x7f, 0x00, 0x00};
47 static unsigned char rarr_bits[] = {
48    0xfc, 0x1f, 0xfe, 0x3f, 0x07, 0x70, 0x13, 0x60, 0x73, 0x60, 0xf3, 0x61,
49    0xf3, 0x67, 0xf3, 0x6f, 0xf3, 0x67, 0xf3, 0x61, 0x73, 0x60, 0x13, 0x60,
50    0x07, 0x70, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00};
51 static unsigned char riarr_bits[] = {
52    0xfc, 0x1f, 0x06, 0x30, 0xfb, 0x6f, 0xed, 0x5f, 0x8d, 0x5f, 0x0d, 0x5e,
53    0x0d, 0x58, 0x0d, 0x50, 0x0d, 0x58, 0x0d, 0x5e, 0x8d, 0x5f, 0xed, 0x5f,
54    0xfb, 0x6f, 0x06, 0x30, 0xfc, 0x1f, 0x00, 0x00};
55 static unsigned char larr_bits[] = {
56    0xfc, 0x1f, 0xfe, 0x3f, 0x07, 0x70, 0x03, 0x64, 0x03, 0x67, 0xc3, 0x67,
57    0xf3, 0x67, 0xfb, 0x67, 0xf3, 0x67, 0xc3, 0x67, 0x03, 0x67, 0x03, 0x64,
58    0x07, 0x70, 0xfe, 0x3f, 0xfc, 0x1f, 0x00, 0x00};
59 static unsigned char liarr_bits[] = {
60    0xfc, 0x1f, 0x06, 0x30, 0xfb, 0x6f, 0xfd, 0x5b, 0xfd, 0x58, 0x3d, 0x58,
61    0x0d, 0x58, 0x05, 0x58, 0x0d, 0x58, 0x3d, 0x58, 0xfd, 0x58, 0xfd, 0x5b,
62    0xfb, 0x6f, 0x06, 0x30, 0xfc, 0x1f, 0x00, 0x00};
63 
64 /* Types of objects */
65 typedef struct {
66     Window win;
67     int ulx, uly, lrx, lry;
68     int idno;
69     char name[NAME_LEN];
70     boolean state;
71 } button;
72 
73 /* Types of objects */
74 typedef struct {
75     Window win;
76     int ulx, uly, lrx, lry;
77     int idno;
78     char name[NAME_LEN];
79     int *activeid;
80     Pixmap pm;
81     int pmwid, pmht;
82     boolean state;
83     boolean disable;
84 } modebutton;
85 
86 typedef struct {
87     Window win;
88     int ulx, uly, lrx, lry;
89     int idno;
90     char name[NAME_LEN];
91     boolean nowstate;
92     boolean *state;
93 } checkbox;
94 
95 typedef struct {
96     Window win;
97     int ulx, uly, lrx, lry;
98     int idno;
99     char name[NAME_LEN];
100     char format[NAME_LEN];
101     int state, active;
102     double nowvalue;
103     double *value;
104     double vmin, vmax, vincr;
105 } slider;
106 
107 /* Object globals */
108 static button buttons[MAX_OBJS];
109 static modebutton mbuttons[MAX_OBJS];
110 static checkbox cboxes[MAX_OBJS];
111 static slider sliders[MAX_OBJS];
112 static char keybuff[NUM_DIGS + 1];
113 static int numb, numm, nums, numc, cur_type, cur_num, cur_but, key_active;
114 static boolean key_dirty;
115 static Pixmap cb_pm, cbc_pm, cbcb_pm, cbub_pm, la_pm, lap_pm, ra_pm, rap_pm;
116 
117 /* Flag if the arrow buttons on sliders are in scan mode */
118 boolean scan_flag;
119 
120 /* X variables from elsewhere */
121 extern Display *dpy;
122 extern Window main_win;
123 extern GC drawgc, erasegc, fggc, bggc, revgc, hlgc;
124 extern Pixmap acts_pm;
125 void (*notify_func)();
126 
127 Pixmap get_pixmap(char *bits, int width, int height, boolean inv);
128 
129 void init_widgets(notify)
130 void (*notify)();
131 {
132     numb = nums = numc = numm = cur_type = cur_num = 0;
133     key_active = cur_but = -1;
134     scan_flag = FALSE;
135 
136     notify_func = notify;
137 
138     cb_pm = get_pixmap(box_bits, 16, 16, FALSE);
139     cbc_pm = get_pixmap(check_bits, 16, 16, FALSE);
140     cbcb_pm = get_pixmap(checked_bits, 16, 16, FALSE);
141     cbub_pm = get_pixmap(unchecked_bits, 16, 16, FALSE);
142     la_pm = get_pixmap(larr_bits, 16, 16, FALSE);
143     lap_pm = get_pixmap(liarr_bits, 16, 16, FALSE);
144     ra_pm = get_pixmap(rarr_bits, 16, 16, FALSE);
145     rap_pm = get_pixmap(riarr_bits, 16, 16, FALSE);
146 }
147 
add_button(d,win,ulx,uly,lrx,lry,name,idno)148 void add_button(d, win, ulx, uly, lrx, lry, name, idno)
149 Drawable d;
150 Window win;
151 int ulx, uly, lrx, lry;
152 char *name;
153 int idno;
154 {
155     int len;
156 
157     if (numb < MAX_OBJS - 1) {
158 	buttons[numb].win = win;
159 	buttons[numb].ulx = ulx;
160 	buttons[numb].uly = uly;
161 	buttons[numb].lrx = lrx;
162 	buttons[numb].lry = lry;
163 	strncpy(buttons[numb].name, name, NAME_LEN-1);
164 	buttons[numb].name[NAME_LEN-1] = '\0';
165 	len = strlen(buttons[numb].name);
166 	buttons[numb].idno = idno;
167 	buttons[numb].state = FALSE;
168 
169 	XDrawRectangle(dpy, d, drawgc, ulx, uly, lrx - ulx + 1, lry - uly + 1);
170 	XDrawRectangle(dpy, d, drawgc, ulx+1, uly+1, lrx - ulx - 1, lry - uly - 1);
171 
172 	XDrawString(dpy, d, drawgc, (ulx + lrx - len * F_WID) / 2, (uly + lry + F_HT) / 2,
173 		    buttons[numb].name, len);
174 
175 	numb++;
176     }
177 }
178 
draw_modebutton(d,which)179 void draw_modebutton(d, which)
180 Drawable d;
181 int which;
182 {
183     int ulx, uly, lrx, lry, pmwid, pmht;
184     int len;
185     Pixmap pm;
186 
187     ulx = mbuttons[which].ulx;
188     uly = mbuttons[which].uly;
189     lrx = mbuttons[which].lrx;
190     lry = mbuttons[which].lry;
191     pmwid = mbuttons[which].pmwid;
192     pmht = mbuttons[which].pmht;
193     pm = mbuttons[which].pm;
194 
195     len = strlen(mbuttons[which].name);
196 
197     if (len) {
198 	XCopyArea(dpy, pm, d, drawgc, 0, 0, pmwid, pmht, ulx + (lrx - ulx - pmwid) / 2, uly + (lry - uly - 4 * F_HT / 3 - 1 - pmht) / 2);
199 	XDrawString(dpy, d, drawgc, ulx + (lrx - ulx - len * F_WID) / 2, lry - F_HT / 3 - 1, mbuttons[which].name, len);
200     } else {
201 	XCopyArea(dpy, pm, d, drawgc, 0, 0, pmwid, pmht, ulx + (lrx - ulx - pmwid) / 2, uly + (lry - uly - pmht) / 2);
202     }
203 }
204 
add_modebutton(d,win,ulx,uly,lrx,lry,name,pm_bits,pmwid,pmht,idno,activeid,disable)205 void add_modebutton(d, win, ulx, uly, lrx, lry, name, pm_bits, pmwid, pmht, idno, activeid, disable)
206 Drawable d;
207 Window win;
208 int ulx, uly, lrx, lry;
209 char *name;
210 char *pm_bits;
211 int pmwid, pmht;
212 int idno;
213 int *activeid;
214 boolean disable;
215 {
216     if (numm < MAX_OBJS - 1) {
217 	mbuttons[numm].win = win;
218 	mbuttons[numm].ulx = ulx;
219 	mbuttons[numm].uly = uly;
220 	mbuttons[numm].lrx = lrx;
221 	mbuttons[numm].lry = lry;
222 	strncpy(mbuttons[numm].name, name, NAME_LEN-1);
223 	mbuttons[numm].name[NAME_LEN-1] = '\0';
224 	mbuttons[numm].idno = idno;
225 	mbuttons[numm].activeid = activeid;
226 	mbuttons[numm].state = (mbuttons[numm].idno == *activeid);
227 
228 	mbuttons[numm].disable = disable;
229 
230 	XDrawRectangle(dpy, d, drawgc, ulx, uly, lrx - ulx + 1, lry - uly + 1);
231 	XDrawRectangle(dpy, d, drawgc, ulx+1, uly+1, lrx - ulx - 1, lry - uly - 1);
232 
233 	mbuttons[numm].pm = get_pixmap(pm_bits, pmwid, pmht, FALSE);
234 	mbuttons[numm].pmwid = pmwid;
235 	mbuttons[numm].pmht = pmht;
236 
237 	draw_modebutton(d, numm);
238 
239 	numm++;
240     }
241 }
242 
add_checkbox(d,win,ulx,uly,lrx,lry,name,idno,state)243 void add_checkbox(d, win, ulx, uly, lrx, lry, name, idno, state)
244 Drawable d;
245 Window win;
246 int ulx, uly, lrx, lry;
247 char *name;
248 int idno;
249 boolean *state;
250 {
251     int len;
252 
253     if (numc < MAX_OBJS - 1) {
254 	cboxes[numc].win = win;
255 	cboxes[numc].ulx = ulx;
256 	cboxes[numc].uly = uly;
257 	cboxes[numc].lrx = lrx;
258 	cboxes[numc].lry = lry;
259 	strncpy(cboxes[numc].name, name, NAME_LEN-1);
260 	cboxes[numc].name[NAME_LEN-1] = '\0';
261 	len = strlen(cboxes[numc].name);
262 	cboxes[numc].idno = idno;
263 	cboxes[numc].state = state;
264 	cboxes[numc].nowstate = *(cboxes[numc].state);
265 
266 	XCopyArea(dpy, cb_pm, d, drawgc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2);
267 
268 	XDrawString(dpy, d, drawgc, ulx + 20, (uly + lry + F_HT) / 2 - 1, cboxes[numc].name, len);
269 
270 	numc++;
271     }
272 }
273 
add_slider(d,win,ulx,uly,lrx,lry,name,format,idno,value,vmax,vmin,vincr)274 void add_slider(d, win, ulx, uly, lrx, lry, name, format, idno, value, vmax, vmin, vincr)
275 Drawable d;
276 Window win;
277 int ulx, uly, lrx, lry;
278 char *name, *format;
279 int idno;
280 double *value;
281 double vmin, vmax, vincr;
282 {
283     int len;
284 
285     if (numc < MAX_OBJS - 1) {
286 	sliders[nums].win = win;
287 	sliders[nums].ulx = ulx;
288 	sliders[nums].uly = uly;
289 	sliders[nums].lrx = lrx;
290 	sliders[nums].lry = lry;
291 	strncpy(sliders[nums].name, name, NAME_LEN-1);
292 	sliders[nums].name[NAME_LEN-1] = '\0';
293 	len = strlen(sliders[nums].name);
294 	strncpy(sliders[nums].format, format, NAME_LEN-1);
295 	sliders[nums].format[NAME_LEN-1] = '\0';
296 	sliders[nums].idno = idno;
297 	sliders[nums].value = value;
298 	sliders[nums].nowvalue = *(sliders[nums].value);
299 	sliders[nums].vmax = vmax;
300 	sliders[nums].vmin = vmin;
301 	sliders[nums].vincr = vincr;
302 	sliders[nums].state = O_NOTHING;
303 	sliders[nums].active = FALSE;
304 
305 	XCopyArea(dpy, la_pm, d, drawgc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2);
306 	XCopyArea(dpy, ra_pm, d, drawgc, 0, 0, 16, 16, ulx + 16 + F_WID * NUM_DIGS + 12, (uly + lry - 16) / 2);
307 
308 	XDrawRectangle(dpy, d, drawgc, ulx + 16 + 2, (uly + lry - 16) / 2, F_WID * NUM_DIGS + 6, F_HT + 4);
309 	XDrawRectangle(dpy, d, drawgc, ulx + 16 + 3, (uly + lry - 16) / 2 + 1, F_WID * NUM_DIGS + 4, F_HT + 2);
310 
311 	XDrawString(dpy, d, drawgc, ulx + 16 + F_WID * NUM_DIGS + 32, (uly + lry + F_HT) / 2 - 2, sliders[nums].name, len);
312 
313 	nums++;
314     }
315 }
316 
activate_mbutton(activeptr,state)317 void activate_mbutton(activeptr, state)
318 int *activeptr;
319 boolean state;
320 {
321     int i;
322 
323     for (i = 0; i < numm; i++) {
324 	if (mbuttons[i].activeid == activeptr) {
325 	    *(mbuttons[i].activeid) = state ? mbuttons[i].idno : -1;
326 	    break;
327 	}
328     }
329 }
330 
update_mbutton(old_id,new_id)331 static void update_mbutton(old_id, new_id)
332 int old_id, new_id;
333 {
334     int i, ulx, uly, lrx, lry;
335 
336     /* Invert old mode button */
337     for (i = 0; i < numm; i++) {
338 	if (mbuttons[i].idno == old_id)
339 	  break;
340     }
341     if (mbuttons[i].idno == old_id) {
342 	ulx = mbuttons[i].ulx;
343 	uly = mbuttons[i].uly;
344 	lrx = mbuttons[i].lrx;
345 	lry = mbuttons[i].lry;
346 
347 	XCopyPlane(dpy, acts_pm, mbuttons[i].win, fggc, ulx+3, uly+3, lrx - ulx - 4, lry - uly - 4, ulx+3, uly+3, 0x1);
348     }
349 
350     /* Invert new mode button */
351     for (i = 0; i < numm; i++) {
352 	if (mbuttons[i].idno == new_id)
353 	  break;
354     }
355     if (mbuttons[i].idno == new_id) {
356 	ulx = mbuttons[i].ulx;
357 	uly = mbuttons[i].uly;
358 	lrx = mbuttons[i].lrx;
359 	lry = mbuttons[i].lry;
360 
361 	XCopyPlane(dpy, acts_pm, mbuttons[i].win, revgc, ulx+3, uly+3, lrx - ulx - 4, lry - uly - 4, ulx+3, uly+3, 0x1);
362     }
363 }
364 
update_slider_box(cur,ulx,uly,lrx,lry,inverted)365 void update_slider_box(cur, ulx, uly, lrx, lry, inverted)
366 int cur;
367 boolean inverted;
368 {
369     int len;
370     char valuebuf[256];
371 
372     if (cur == key_active && key_dirty) {
373 	strcpy(valuebuf, keybuff);
374 	len = strlen(valuebuf);
375     } else {
376 	sprintf(valuebuf, sliders[cur].format, *(sliders[cur].value));
377 	if ((len = strlen(valuebuf)) > NUM_DIGS)
378 	  len = NUM_DIGS;
379     }
380 
381     XFillRectangle(dpy, sliders[cur].win, bggc, ulx + 16 + 4, (uly + lry - 16) / 2 + 2, F_WID * NUM_DIGS + 3, F_HT + 1);
382 
383     if (inverted) {
384 	XFillRectangle(dpy, sliders[cur].win, hlgc, ulx + 16 + 5, (uly + lry - 16) / 2 + 3, F_WID * NUM_DIGS + 1, F_HT - 1);
385     }
386     XDrawString(dpy, sliders[cur].win, inverted ? bggc : fggc, ulx + 16 + 4, (uly + lry + F_HT) / 2 - 2, valuebuf, len);
387 }
388 
update_slider(cur)389 void update_slider(cur)
390 int cur;
391 {
392     int ulx, uly, lrx, lry;
393 
394     ulx = sliders[cur].ulx;
395     uly = sliders[cur].uly;
396     lrx = sliders[cur].lrx;
397     lry = sliders[cur].lry;
398 
399     /* Set to proper range */
400     if (*(sliders[cur].value) < sliders[cur].vmin) {
401 	*(sliders[cur].value) = sliders[cur].vmin;
402     } else if (*(sliders[cur].value) > sliders[cur].vmax) {
403 	*(sliders[cur].value) = sliders[cur].vmax;
404     }
405 
406     update_slider_box(cur, ulx, uly, lrx, lry, cur == key_active);
407 }
408 
change_slider_parms(cur,valp,max,min)409 void change_slider_parms(cur, valp, max, min)
410 int cur;
411 double *valp, max, min;
412 {
413     sliders[cur].value = valp;
414     sliders[cur].vmax = max;
415     sliders[cur].vmin = min;
416 }
417 
slider_valno(idno)418 int slider_valno(idno)
419 int idno;
420 {
421     int i;
422 
423     /* Draw sliders */
424     for (i = 0; i < nums; i++) {
425 	if (sliders[i].idno == idno)
426 	  return i;
427     }
428     return -1;
429 }
430 
update_checkbox(cur,active)431 static void update_checkbox(cur, active)
432 int cur;
433 boolean active;
434 {
435     int ulx, uly, lry;
436     Pixmap which_pm;
437 
438     ulx = cboxes[cur].ulx;
439     uly = cboxes[cur].uly;
440     lry = cboxes[cur].lry;
441 
442     if (active) {
443 	which_pm = cboxes[cur].nowstate ? cbub_pm : cbcb_pm;
444     } else {
445 	which_pm = *(cboxes[cur].state) ? cbc_pm : cb_pm;
446     }
447 
448     XCopyPlane(dpy, which_pm, cboxes[cur].win, fggc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2, 0x1);
449 }
450 
redraw_widgets(mode)451 void redraw_widgets(mode)
452 boolean mode;
453 {
454     int i;
455 
456     if (mode){
457 	/* Draw mode buttons */
458 	for (i = 0; i < numm; i++) {
459 	    if (*(mbuttons[i].activeid) == mbuttons[i].idno)
460 	      update_mbutton(-1, mbuttons[i].idno);
461 	}
462     }
463 
464     /* Draw checkboxes */
465     for (i = 0; i < numc; i++) {
466 	update_checkbox(i, FALSE);
467     }
468 
469     /* Draw sliders */
470     for (i = 0; i < nums; i++) {
471 	update_slider(i);
472     }
473 
474 
475     /* Redraw active object */
476 }
477 
key_widgets(key,win)478 boolean key_widgets(key, win)
479 int key;
480 Window win;
481 {
482     int len, ka = key_active;
483 
484     if (key_active < 0 || sliders[key_active].win != win)
485       return FALSE;
486 
487     len = strlen(keybuff);
488 
489     switch (key) {
490       case K_DELETE:
491 	if (len > 0) {
492 	    keybuff[len - 1] = '\0';
493 	}
494 	break;
495       case K_RETURN:
496 	if (keybuff[0]) {
497 	    sscanf(keybuff, "%lf", sliders[key_active].value);
498 	}
499       case K_ESCAPE:
500 	key_active = -1;
501 	break;
502       default:
503 	if (len < NUM_DIGS - 1) {
504 	    keybuff[len] = (char)key;
505 	    keybuff[len+1] = '\0';
506 	}
507 	break;
508     }
509 
510     key_dirty = TRUE;
511     update_slider(ka);
512     if (key == K_RETURN) {
513 	notify_func(O_SLIDER, ka);
514     }
515     return TRUE;
516 }
517 
check_widgets(win,mx,my,butn,mstat)518 boolean check_widgets(win, mx, my, butn, mstat)
519 Window win;
520 int mx, my;
521 int butn, mstat;
522 {
523     int i;
524     int ulx, uly, lrx, lry;
525 
526     switch (mstat) {
527       case M_UP:
528 	/* If button up, then just deactivate current widget */
529 	if (cur_but == butn) {
530 	    switch (cur_type) {
531 	      case O_BUTTON:
532 		ulx = buttons[cur_num].ulx;
533 		uly = buttons[cur_num].uly;
534 		lrx = buttons[cur_num].lrx;
535 		lry = buttons[cur_num].lry;
536 
537 		if (buttons[cur_num].state) {
538 		    XCopyPlane(dpy, acts_pm, win, fggc, ulx+3, uly+3, lrx - ulx - 4, lry - uly - 4, ulx+3, uly+3, 0x1);
539 		    buttons[cur_num].state = FALSE;
540 		    notify_func(O_BUTTON, buttons[cur_num].idno);
541 		}
542 		break;
543 
544 	      case O_MBUTTON:
545 		if (mbuttons[cur_num].state) {
546 		    *(mbuttons[cur_num].activeid) = (*(mbuttons[cur_num].activeid) != mbuttons[cur_num].idno) ? mbuttons[cur_num].idno : -1;
547 		    mbuttons[cur_num].state = FALSE;
548 		    notify_func(O_MBUTTON, mbuttons[cur_num].idno);
549 		}
550 		break;
551 
552 	      case O_SLIDER:
553 		ulx = sliders[cur_num].ulx;
554 		uly = sliders[cur_num].uly;
555 		lrx = sliders[cur_num].lrx;
556 		lry = sliders[cur_num].lry;
557 
558 		if (sliders[cur_num].state == O_LSLIDER) {
559 		    XCopyPlane(dpy, la_pm, win, fggc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2, 0x1);
560 		    sliders[cur_num].active = FALSE;
561 		    notify_func(O_SLIDER, sliders[cur_num].idno);
562 		} else if (sliders[cur_num].state == O_RSLIDER) {
563 		    XCopyPlane(dpy, ra_pm, win, fggc, 0, 0, 16, 16, ulx + 16 + F_WID * NUM_DIGS + 12, (uly + lry - 16) / 2, 0x1);
564 		    sliders[cur_num].active = FALSE;
565 		    notify_func(O_SLIDER, sliders[cur_num].idno);
566 		} else if (sliders[cur_num].state == O_TSLIDER) {
567 		    if (sliders[cur_num].active) {
568 			key_active = cur_num;
569 			key_dirty = FALSE;
570 			keybuff[0] = '\0';
571 		    }
572 		}
573 
574 		scan_flag = FALSE;
575 		break;
576 
577 	      case O_CHECKBOX:
578 		ulx = cboxes[cur_num].ulx;
579 		uly = cboxes[cur_num].uly;
580 		lrx = cboxes[cur_num].lrx;
581 		lry = cboxes[cur_num].lry;
582 
583 		if (*(cboxes[cur_num].state) != cboxes[cur_num].nowstate) {
584 		    *(cboxes[cur_num].state) = cboxes[cur_num].nowstate;
585 		    update_checkbox(cur_num, FALSE);
586 
587 		    notify_func(O_CHECKBOX, cboxes[cur_num].idno);
588 		}
589 		break;
590 	    }
591 	    cur_type = cur_num = 0;
592 	    cur_but = -1;
593 	    return TRUE;
594 	}
595 	break;
596 
597       case M_DOWN:
598 	if (cur_but < 0) {
599 	    /* Check buttons */
600 	    for (i = 0; i < numb; i++) {
601 		ulx = buttons[i].ulx;
602 
603 		uly = buttons[i].uly;
604 		lrx = buttons[i].lrx;
605 		lry = buttons[i].lry;
606 
607 		if (buttons[i].win == win && ulx < mx && mx < lrx && uly < my && my < lry) {
608 		    if (!buttons[i].state) {
609 			XCopyPlane(dpy, acts_pm, win, revgc, ulx+3, uly+3, lrx - ulx - 4, lry - uly - 4, ulx+3, uly+3, 0x1);
610 			buttons[i].state = TRUE;
611 		    }
612 		    cur_type = O_BUTTON;
613 		    cur_num = i;
614 		    cur_but = butn;
615 		    return TRUE;
616 		}
617 	    }
618 
619 	    /* Check checkboxes */
620 	    for (i = 0; i < numc; i++) {
621 		ulx = cboxes[i].ulx;
622 		uly = cboxes[i].uly;
623 		lrx = cboxes[i].lrx;
624 		lry = cboxes[i].lry;
625 
626 		if (cboxes[i].win == win && ulx < mx && mx < lrx && uly < my && my < lry) {
627 		    if (*(cboxes[i].state)) {
628 			cboxes[i].nowstate = FALSE;
629 		    } else {
630 			cboxes[i].nowstate = TRUE;
631 		    }
632 		    update_checkbox(i, TRUE);
633 
634 		    cur_type = O_CHECKBOX;
635 		    cur_num = i;
636 		    cur_but = butn;
637 		    return TRUE;
638 		}
639 	    }
640 
641 	    /* Check mode buttons */
642 	    for (i = 0; i < numm; i++) {
643 		ulx = mbuttons[i].ulx;
644 		uly = mbuttons[i].uly;
645 		lrx = mbuttons[i].lrx;
646 		lry = mbuttons[i].lry;
647 
648 		if (mbuttons[i].win == win && ulx < mx && mx < lrx && uly < my && my < lry) {
649 		    if ((*(mbuttons[i].activeid) == mbuttons[i].idno) && !mbuttons[i].disable)
650 		      return FALSE;
651 
652 		    update_mbutton(*(mbuttons[i].activeid), (*(mbuttons[i].activeid) != mbuttons[i].idno) ? mbuttons[i].idno : -1);
653 		    mbuttons[i].state = TRUE;
654 
655 		    cur_type = O_MBUTTON;
656 		    cur_num = i;
657 		    cur_but = butn;
658 		    return TRUE;
659 		}
660 	    }
661 
662 	    /* Check sliders */
663 	    for (i = 0; i < nums; i++) {
664 		ulx = sliders[i].ulx;
665 		uly = sliders[i].uly;
666 		lrx = sliders[i].lrx;
667 		lry = sliders[i].lry;
668 
669 		if (sliders[i].win == win && ulx < mx && (uly + lry - 16) / 2 < my && my < (uly + lry - 16) / 2 + 16) {
670 		    if (mx < ulx + 16) {
671 			/* Do left arrow */
672 			if (i == key_active) {
673 			    key_widgets(K_RETURN, sliders[key_active].win);
674 			}
675 			sliders[i].state = O_LSLIDER;
676 			XCopyPlane(dpy, lap_pm, win, fggc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2, 0x1);
677 			if (butn == 1)
678 			  *(sliders[i].value) -= sliders[i].vincr;
679 			update_slider(i);
680 			sliders[i].active = TRUE;
681 			if (butn != 1)
682 			  scan_flag = TRUE;
683 		    } else if (ulx + 16 + F_WID * NUM_DIGS + 12 < mx && mx < ulx + 16 + F_WID * NUM_DIGS + 12 + 16) {
684 			/* Do right arrow */
685 			if (i == key_active) {
686 			    key_widgets(K_RETURN, sliders[key_active].win);
687 			}
688 			sliders[i].state = O_RSLIDER;
689 			XCopyPlane(dpy, rap_pm, win, fggc, 0, 0, 16, 16, ulx + 16 + F_WID * NUM_DIGS + 12, (uly + lry - 16) / 2, 0x1);
690 			if (butn == 1)
691 			  *(sliders[i].value) += sliders[i].vincr;
692 			update_slider(i);
693 			sliders[i].active = TRUE;
694 			if (butn != 1)
695 			  scan_flag = TRUE;
696 		    } else if (ulx + 18 < mx && mx < ulx + 18 + F_WID * NUM_DIGS + 6) {
697 			/* Do text box */
698 			key_widgets(K_RETURN, sliders[key_active].win);
699 			sliders[i].state = O_TSLIDER;
700 			sliders[i].active = TRUE;
701 			update_slider_box(i, ulx, uly, lrx, lry, TRUE);
702 		    }
703 
704 		    cur_type = O_SLIDER;
705 		    cur_num = i;
706 		    cur_but = butn;
707 		    return TRUE;
708 		}
709 	    }
710 
711 	}
712 	break;
713 
714       case M_DRAG:
715 	if (cur_but >= 0) {
716 	    boolean inside;
717 
718 	    switch (cur_type) {
719 	      case O_BUTTON:
720 		ulx = buttons[cur_num].ulx;
721 		uly = buttons[cur_num].uly;
722 		lrx = buttons[cur_num].lrx;
723 		lry = buttons[cur_num].lry;
724 
725 		inside = (buttons[cur_num].win == win) && ulx < mx && mx < lrx && uly < my && my < lry;
726 
727 		if ((inside && !buttons[cur_num].state) || (!inside && buttons[cur_num].state)) {
728 		    /* Flip button value */
729 		    XCopyPlane(dpy, acts_pm, win, buttons[cur_num].state ? fggc : revgc, ulx+3, uly+3, lrx - ulx - 4, lry - uly - 4, ulx+3, uly+3, 0x1);
730 		    buttons[cur_num].state = !buttons[cur_num].state;
731 		}
732 		break;
733 
734 	      case O_MBUTTON:
735 		ulx = mbuttons[cur_num].ulx;
736 		uly = mbuttons[cur_num].uly;
737 		lrx = mbuttons[cur_num].lrx;
738 		lry = mbuttons[cur_num].lry;
739 
740 		inside = (mbuttons[cur_num].win == win) && ulx < mx && mx < lrx && uly < my && my < lry;
741 
742 		if ((inside && !mbuttons[cur_num].state) || (!inside && mbuttons[cur_num].state)) {
743 		    /* Flip button value */
744 		    if (mbuttons[cur_num].state)
745 		      update_mbutton((*(mbuttons[cur_num].activeid) != mbuttons[cur_num].idno) ? mbuttons[cur_num].idno : -1, *(mbuttons[cur_num].activeid));
746 		    else
747 		      update_mbutton(*(mbuttons[cur_num].activeid), (*(mbuttons[cur_num].activeid) != mbuttons[cur_num].idno) ? mbuttons[cur_num].idno : -1);
748 
749 		    mbuttons[cur_num].state = !mbuttons[cur_num].state;
750 		}
751 		break;
752 
753 	      case O_SLIDER:
754 		ulx = sliders[cur_num].ulx;
755 		uly = sliders[cur_num].uly;
756 		lrx = sliders[cur_num].lrx;
757 		lry = sliders[cur_num].lry;
758 
759 		inside = ((uly + lry - 16) / 2 < my && my < (uly + lry - 16) / 2 + 16);
760 
761 		if (sliders[cur_num].state == O_LSLIDER) {
762 		    inside = inside && (ulx < mx) && (mx < ulx + 16);
763 
764 		    if (sliders[cur_num].active && !inside) {
765 			XCopyPlane(dpy, la_pm, win, fggc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2, 0x1);
766 			sliders[cur_num].active = FALSE;
767 		    } else if (!sliders[cur_num].active && inside) {
768 			XCopyPlane(dpy, lap_pm, win, fggc, 0, 0, 16, 16, ulx, (uly + lry - 16) / 2, 0x1);
769 			sliders[cur_num].active = TRUE;
770 		    }
771 		    scan_flag = (cur_but != 1 && sliders[cur_num].active);
772 		} else if (sliders[cur_num].state == O_RSLIDER) {
773 		    inside = inside && (ulx + 16 + F_WID * NUM_DIGS + 12 < mx) && (mx < ulx + 16 + F_WID * NUM_DIGS + 12 + 16);
774 
775 		    if (sliders[cur_num].active && !inside) {
776 			XCopyPlane(dpy, ra_pm, win, fggc, 0, 0, 16, 16, ulx + 16 + F_WID * NUM_DIGS + 12, (uly + lry - 16) / 2, 0x1);
777 			sliders[cur_num].active = FALSE;
778 		    } else if (!sliders[cur_num].active && inside) {
779 			XCopyPlane(dpy, rap_pm, win, fggc, 0, 0, 16, 16, ulx + 16 + F_WID * NUM_DIGS + 12, (uly + lry - 16) / 2, 0x1);
780 			sliders[cur_num].active = TRUE;
781 		    }
782 		    scan_flag = (cur_but != 1 && sliders[cur_num].active);
783 		} else if (sliders[cur_num].state == O_TSLIDER) {
784 		    inside = (uly + lry - 16) / 2 < my && my <= (uly + lry - 16) / 2 + F_HT + 3 && ulx + 18 < mx && mx < ulx + 18 + F_WID * NUM_DIGS + 6;
785 
786 		    if ((sliders[cur_num].active && !inside) || (!sliders[cur_num].active && inside)) {
787 			update_slider_box(cur_num, ulx, uly, lrx, lry, inside);
788 			sliders[cur_num].active = !sliders[cur_num].active;
789 		    }
790 		}
791 		break;
792 
793 	      case O_CHECKBOX:
794 		ulx = cboxes[cur_num].ulx;
795 		uly = cboxes[cur_num].uly;
796 		lrx = cboxes[cur_num].lrx;
797 		lry = cboxes[cur_num].lry;
798 
799 		inside = (cboxes[cur_num].win == win) && ulx < mx && mx < lrx && uly < my && my < lry;
800 
801 		if (inside && *(cboxes[cur_num].state) == cboxes[cur_num].nowstate) {
802 		    cboxes[cur_num].nowstate = !*(cboxes[cur_num].state);
803 		} else if (!inside && *(cboxes[cur_num].state) != cboxes[cur_num].nowstate) {
804 		    cboxes[cur_num].nowstate = *(cboxes[cur_num].state);
805 		} else {
806 		    break;
807 		}
808 
809 		/* Change checkbox value */
810 		update_checkbox(cur_num, inside);
811 		break;
812 	    }
813 	}
814 	break;
815 
816       case M_HOLD:
817 	if (cur_but >= 1 && cur_type == O_SLIDER) {
818 	    double mag = 1.0;
819 
820 	    if (cur_but == 3)
821 	      mag = 10.0;
822 
823 	    if (sliders[cur_num].state == O_LSLIDER) {
824 		*(sliders[cur_num].value) -= sliders[cur_num].vincr * mag;
825 		update_slider(cur_num);
826 	    } else if (sliders[cur_num].state == O_RSLIDER) {
827 		*(sliders[cur_num].value) += sliders[cur_num].vincr * mag;
828 		update_slider(cur_num);
829 	    }
830 	}
831 	break;
832     }
833 
834     return FALSE;
835 }
836