1 /*
2  * GraphApp - Cross-Platform Graphics Programming Library.
3  *
4  * File: buttons.c -- creating buttons, scrollbars, etc.
5  * Platform: Windows  Version: 2.41  Date: 1998/06/06
6  *
7  * Version: 1.00  Changes: Original version by Lachlan Patrick.
8  * Version: 1.60  Changes: Minor changes.
9  * Version: 2.00  Changes: New object class system.
10  * Version: 2.10  Changes: Radiogroups fully implemented.
11  * Version: 2.20  Changes: Added non-native buttons.
12  * Version: 2.21  Changes: 32-bit fix by Wim Rijnders.
13  * Version: 2.22  Changes: Added newtextarea function.
14  * Version: 2.23  Changes: Added newpassword function.
15  * Version: 2.30  Changes: Added imagebuttons.
16  * Version: 2.35  Changes: Added newpicture function.
17  * Version: 2.40  Changes: Added new list controls.
18  * Version: 2.41  Changes: Updated imagebuttons.
19  * Version: 2.42  Changes: Fixed drop-down list height.
20  */
21 
22 /* Copyright (C) 1993-1998 Lachlan Patrick
23 
24    This file is part of GraphApp, a cross-platform C graphics library.
25 
26    GraphApp is free software; you can redistribute it and/or modify it
27    under the terms of the GNU Library General Public License.
28    GraphApp is distributed in the hope that it will be useful, but
29    WITHOUT ANY WARRANTY.
30 
31    See the file COPYLIB.TXT for details.
32 */
33 
34 /* Copyright (C) 2004-8  The R Foundation
35 
36    Changes for R:
37 
38    set the system font for labels
39    add newscrollbar, field_no_border
40    add extended selections, pass on de-selections
41 
42  */
43 
44 #include "internal.h"
45 #include "ga.h"
46 #include <richedit.h>
47 
48 #define SHADOW_WIDTH 1
49 
50 #define isarmed(obj) ((obj)->state & GA_Armed)
51 #define arm(obj)     ((obj)->state |= GA_Armed)
52 #define disarm(obj)  ((obj)->state &= ~GA_Armed)
53 
54 #define hasfocus(obj) ((obj)->state & GA_Focus)
55 #define setfocus(obj) SetFocus((obj)->handle)
56 
57 /*
58  *  Ensure there is a current window, create one if there isn't.
59  *  If a window is created, it is also shown on the screen.
60  */
ensure_window(void)61 static void ensure_window(void)
62 {
63     if (! current_window) {
64 	current_window = simple_window();
65 	show(current_window);
66     }
67 }
68 
69 /*
70  *  Save the winproc for later and set our own winproc instead.
71  */
set_new_winproc(object obj)72 static void set_new_winproc(object obj)
73 {
74     HWND hwnd;
75     hwnd = obj->handle;
76 #ifdef _WIN64
77     obj->winproc = (WNDPROC) GetWindowLongPtr(hwnd, GWLP_WNDPROC);
78     SetWindowLongPtr(hwnd, GWLP_WNDPROC, (LONG_PTR) app_control_proc);
79 #else
80     obj->winproc = (WNDPROC) GetWindowLong(hwnd, GWL_WNDPROC);
81     SetWindowLongPtr(hwnd, GWL_WNDPROC, (LONG) app_control_proc);
82 #endif
83 }
84 
85 /*
86  *  Private object destructor.
87  */
private_delcontrol(control obj)88 static void private_delcontrol(control obj)
89 {
90     ShowWindow(obj->handle, SW_HIDE);
91 #if USE_NATIVE_CONTROLS
92     if (obj->bgbrush)
93 	DeleteObject(obj->bgbrush);
94 #endif
95     DestroyWindow(obj->handle);
96 }
97 
98 /*
99  *  Creating controls.
100  */
newcontrol(const char * text,rect r)101 control newcontrol(const char *text, rect r)
102 {
103     control obj;
104 
105     ensure_window();
106     obj = newwindow(text, r, ChildWindow | TrackMouse);
107     if (obj) {
108 	obj->kind = UserObject;
109 	obj->die = private_delcontrol;
110     }
111     set_new_winproc(obj); /* set custom winproc */
112     show(obj);
113 
114     return obj;
115 }
116 
newdrawing(rect r,drawfn fn)117 drawing newdrawing(rect r, drawfn fn)
118 {
119     drawing obj = newcontrol(NULL, r);
120     if (obj) {
121 	setredraw(obj, fn);
122 	show(obj);
123 	draw(obj);
124     }
125     return obj;
126 }
127 
newchildwin(const char * kind,const char * text,unsigned long style,rect r,actionfn fn)128 static object newchildwin(const char *kind, const char *text,
129 			  unsigned long style, rect r, actionfn fn)
130 {
131     HWND hwnd;
132     object obj;
133 
134     ensure_window();
135     r = rcanon(r);
136 
137     if(localeCP > 0 && (localeCP != GetACP())) {
138 	/* This seems not actually to work */
139 	wchar_t wkind[100], wc[1000];
140 	mbstowcs(wkind, kind, 100);
141 	mbstowcs(wc, text, 1000);
142 	hwnd = CreateWindowW(wkind, wc,
143 			     (WS_CHILD | WS_VISIBLE) | style,
144 			     r.x, r.y, r.width, r.height,
145 			     current_window->handle,
146 			     (HMENU) child_id, this_instance, NULL);
147     } else
148 	hwnd = CreateWindow(kind, text,
149 			    (WS_CHILD | WS_VISIBLE) | style,
150 			    r.x, r.y, r.width, r.height,
151 			    current_window->handle,
152 			    (HMENU) child_id, this_instance, NULL);
153 
154 
155     obj = new_object(ControlObject, hwnd, current_window);
156     if (! obj) {
157 	DestroyWindow(hwnd);
158 	return NULL;
159     }
160     obj->die = private_delcontrol;
161     obj->rect = r;
162     obj->id = child_id++;
163     obj->action = fn;
164     obj->state = (GA_Visible | GA_Enabled);
165     obj->flags = ChildWindow;
166     obj->text = new_string(text);
167     set_new_winproc(obj); /* set custom winproc */
168     settextfont(obj, SystemFont);
169     return obj;
170 }
171 
172 /*
173  *  The old native version of newlabel.
174  */
175 #if USE_NATIVE_LABELS
newlabel(const char * text,rect r,int alignment)176 label newlabel(const char *text, rect r, int alignment)
177 {
178     label obj;
179     unsigned long style = SS_LEFT;
180 
181     if ((alignment & AlignRight) == AlignRight)
182 	style = SS_RIGHT;
183     if ((alignment & Center) == Center)
184 	style = SS_CENTER;
185 
186     obj = newchildwin("static", text, style, r, NULL);
187     obj->kind = LabelObject;
188     setbackground(obj, getbackground(parentwindow(obj)));
189     return obj;
190 }
191 #else
192 /*
193  *  Private label redraw function.
194  */
draw_label(control c,rect r)195 static void draw_label(control c, rect r)
196 {
197     /* Draw the label. */
198     if (!isenabled(c))
199 	setcolour(Grey);
200     else
201 	setcolour(Black);
202     setfont(gettextfont(c));
203     drawtext(r, getvalue(c), getname(c));
204 }
205 
206 /*
207  *  Create a new static text label. Now implemented using
208  *  GraphApp code instead of native MS-Windows "static"
209  *  text. This gives more flexibility, better cross-platform
210  *  support and there are no 'look and feel' issues with
211  *  labels, so no problems are introduced by doing this.
212  */
newlabel(const char * text,rect r,int alignment)213 control newlabel(const char *text, rect r, int alignment)
214 {
215     control obj = newcontrol(text, r);
216     if (obj) {
217 	obj->kind = LabelObject;
218 	setredraw(obj, draw_label);
219 	setvalue(obj, alignment);
220 	setbackground(obj, getbackground(parentwindow(obj)));
221 	settextfont(obj, SystemFont);
222 	show(obj);
223     }
224     return obj;
225 }
226 #endif
227 
228 /*
229  *  Uncheck any neighbouring radio buttons, up to any bounding
230  *  radiogroup objects, or to the start or end of the list of
231  *  siblings.
232  */
uncheck_neighbours(object obj)233 static void uncheck_neighbours(object obj)
234 {
235     object first = obj->parent->child;
236     object last  = first->prev;
237     object which;
238 
239     for (which=obj; ; which=which->prev) {
240 	if (which->kind == RadiogroupObject)
241 	    break;
242 	if ((which->kind == RadioObject) && (which != obj))
243 	    uncheck(which);
244 	if (which == first)
245 	    break;
246     }
247     for (which=obj; ; which=which->next) {
248 	if (which->kind == RadiogroupObject)
249 	    break;
250 	if ((which->kind == RadioObject) && (which != obj))
251 	    uncheck(which);
252 	if (which == last)
253 	    break;
254     }
255 }
256 
257 /*
258  *  A radiogroup separates radiobuttons from each other.
259  */
newradiogroup(void)260 object newradiogroup(void)
261 {
262     object obj;
263     ensure_window();
264     obj = new_object(RadiogroupObject, NULL, current_window);
265     return obj;
266 }
267 
268 /*
269  *  Draw a button shadow.
270  */
draw_shadow(rect r,rgb col1,rgb col2,int size)271 static void draw_shadow(rect r, rgb col1, rgb col2, int size)
272 {
273     rgb hue = current->hue;
274 
275     /* Draw top-left button border. */
276     setcolour(col1);
277     fillrect(rect(r.x,r.y,r.width,size));
278     fillrect(rect(r.x,r.y,size,r.height));
279 
280     /* Draw bottom-right button border. */
281     setcolour(col2);
282     while (size > 0) {
283 	fillrect(rect(r.x+r.width-1,r.y,1,r.height));
284 	fillrect(rect(r.x,r.y+r.height-1,r.width,1));
285 	r = insetr(r,1);
286 	size--;
287     }
288     setcolour(hue);
289 }
290 
291 /*
292  *  Various button call-backs.
293  */
button_mousedown(control c,int buttons,point xy)294 static void button_mousedown(control c, int buttons, point xy)
295 {
296     arm(c);
297     highlight(c);
298 }
299 
button_mousemove(control c,int buttons,point xy)300 static void button_mousemove(control c, int buttons, point xy)
301 {
302     if (!isarmed(c))
303 	return;
304     if (buttons && ptinr(xy,getrect(c)))
305 	highlight(c);
306     else
307 	unhighlight(c);
308 }
309 
button_mouseup(control c,int buttons,point xy)310 static void button_mouseup(control c, int buttons, point xy)
311 {
312     if (!isarmed(c))
313 	return;
314     disarm(c);
315     unhighlight(c);
316     if (ptinr(xy, getrect(c)))
317 	activatecontrol(c);
318 }
319 
button_keydown(control c,int ch)320 static void button_keydown(control c, int ch)
321 {
322     if (ch == ' ') {
323 	flashcontrol(c);
324 	activatecontrol(c);
325     }
326 }
327 
328 /*
329  *  Various checkbox call-backs.
330  */
checkbox_mousedown(control c,int buttons,point xy)331 static void checkbox_mousedown(control c, int buttons, point xy)
332 {
333     arm(c);
334     highlight(c);
335 }
336 
checkbox_mousemove(control c,int buttons,point xy)337 static void checkbox_mousemove(control c, int buttons, point xy)
338 {
339     if (!isarmed(c))
340 	return;
341     if (buttons && ptinr(xy,getrect(c)))
342 	highlight(c);
343     else
344 	unhighlight(c);
345 }
346 
checkbox_mouseup(control c,int buttons,point xy)347 static void checkbox_mouseup(control c, int buttons, point xy)
348 {
349     if (! isenabled(c))
350 	return;
351     disarm(c);
352     unhighlight(c);
353     if (! ptinr(xy, getrect(c)))
354 	return;
355     if (ischecked(c))
356 	uncheck(c);
357     else
358 	check(c);
359     activatecontrol(c);
360 }
361 
checkbox_keydown(control c,int ch)362 static void checkbox_keydown(control c, int ch)
363 {
364     if (ch == ' ') {
365 	if (ischecked(c))
366 	    uncheck(c);
367 	else
368 	    check(c);
369 	activatecontrol(c);
370     }
371 }
372 
373 /*
374  *  The imagebutton type:
375  */
376 
setimage(control obj,image img)377 void setimage(control obj, image img)
378 {
379     /* Change the stored image and draw it. */
380     obj->img = img;
381     draw(obj);
382 }
383 
draw_image_button(button obj,rect r)384 static void draw_image_button(button obj, rect r)
385 {
386     image   img;
387     bitmap	store = NULL;
388     rect    ir;
389     rgb     up, down;
390     rgb     old = currentcolour();
391 
392     img = obj->img;
393     if (has_transparent_pixels(img)) {
394 	store = newbitmap(r.width, r.height, 0);
395 	drawto(store);
396 	setcolour(getbackground(obj));
397 	fillrect(r);
398     }
399 
400     if (img) {
401 	ir = insetr(r,2);
402 	if (ishighlighted(obj)) /* button is pressed */
403 	    ir.x += 1, ir.y += 1;
404 
405 	/* Draw the button image. */
406 	if (ischecked(obj))
407 	    drawdarker(img, ir, getrect(img));
408 	else if (isenabled(obj))
409 	    drawimage(img, ir, getrect(img));
410 	else
411 	    drawgreyscale(img, ir, getrect(img));
412 
413 	if (ishighlighted(obj)) { /* fill the gap */
414 	    ir.x -= 1, ir.y -= 1;
415 	    setcolour(getbackground(obj));
416 	    drawline(topleft(ir),topright(ir));
417 	    drawline(topleft(ir),bottomleft(ir));
418 	}
419     }
420 
421     /* Draw button border. */
422     setcolour(getforeground(obj));
423     setlinewidth(1);
424     drawrect(r);
425 
426     /* Draw button shadow. */
427     up = White, down = Grey;
428     if (ishighlighted(obj))
429 	up = Grey, down = LightGrey;
430     draw_shadow(insetr(r,1), up, down, 1);
431 
432     if (store != NULL) {
433 	drawto(obj);
434 	copyrect(store, pt(0,0), getrect(store));
435 	del(store);
436     }
437 
438     setcolour(old);
439 }
440 
441 
newimagebutton(image img,rect r,actionfn fn)442 button newimagebutton(image img, rect r, actionfn fn)
443 {
444     button obj;
445 
446     obj = newcontrol(NULL, r);
447     if (! obj)
448 	return NULL;
449 
450     setredraw(obj, draw_image_button);
451     setmousedown(obj, button_mousedown);
452     setmousemove(obj, button_mousemove);
453     setmousedrag(obj, button_mousemove);
454     setmouseup(obj, button_mouseup);
455     setkeydown(obj, button_keydown);
456     setaction(obj, fn);
457     setbackground(obj, LightGrey);
458     settextfont(obj, SystemFont);
459 
460     setimage(obj, img);
461 
462     show(obj);
463     return obj;
464 }
465 
draw_picture(control obj,rect r)466 static void draw_picture(control obj, rect r)
467 {
468     image   img;
469     bitmap	store = NULL;
470     rgb     old = currentcolour();
471 
472     img = obj->img;
473     if (has_transparent_pixels(img)) {
474 	store = newbitmap(r.width, r.height, 0);
475 	drawto(store);
476 	setcolour(getbackground(obj));
477 	fillrect(r);
478     }
479 
480     if (img) {
481 	/* Draw the button image. */
482 	if (ischecked(obj))
483 	    drawdarker(img, r, getrect(img));
484 	else if (isenabled(obj))
485 	    drawimage(img, r, getrect(img));
486 	else
487 	    drawimage(img, r, getrect(img)); /* never grey */
488     }
489 
490     if (store != NULL) {
491 	drawto(obj);
492 	copyrect(store, pt(0,0), getrect(store));
493 	del(store);
494     }
495 
496     setcolour(old);
497 }
498 
newpicture(image img,rect r)499 control newpicture(image img, rect r)
500 {
501     control obj;
502 
503     obj = newcontrol(NULL, r);
504     if (! obj)
505 	return NULL;
506 
507     setredraw(obj, draw_picture);
508     setimage(obj, img);
509     disable(obj);
510 
511     show(obj);
512     return obj;
513 }
514 
newimagecheckbox(image img,rect r,actionfn fn)515 button newimagecheckbox(image img, rect r, actionfn fn)
516 {
517     button obj;
518 
519     obj = newdrawing(r, draw_image_button);
520     if (! obj)
521 	return NULL;
522 
523     setmousedown(obj, checkbox_mousedown);
524     setmousemove(obj, checkbox_mousemove);
525     setmousedrag(obj, checkbox_mousemove);
526     setmouseup(obj, checkbox_mouseup);
527     setkeydown(obj, checkbox_keydown);
528     setaction(obj, fn);
529     setbackground(obj, LightGrey);
530     settextfont(obj, SystemFont);
531 
532     setimage(obj, img);
533 
534     show(obj);
535     return obj;
536 }
537 
538 
539 #if USE_NATIVE_BUTTONS
newbutton(const char * text,rect r,actionfn fn)540 button newbutton(const char *text, rect r, actionfn fn)
541 {
542     button obj;
543     obj = newchildwin("button", text, BS_PUSHBUTTON, r, fn);
544     if (obj)
545 	obj->kind = ButtonObject;
546     return obj;
547 }
548 #else
draw_button(control c,rect r)549 static void draw_button(control c, rect r)
550 {
551     rect textrect;
552     rgb up, down;
553     font f;
554     rgb old = currentcolour();
555 
556     clear(c);
557 
558     /* Draw the button name. */
559     if (isenabled(c))
560 	setcolour(getforeground(c));
561     else
562 	setcolour(Grey);
563     f = gettextfont(c);
564     setfont(f);
565     textrect = r;
566     if (ishighlighted(c))
567 	textrect.x += 1, textrect.y += 1;
568     drawtext(textrect, Center|VCenter, getname(c));
569 
570     /* Draw button border. */
571     setlinewidth(1);
572     drawrect(r);
573     r = insetr(r,1);
574 
575     /* Draw button shadow. */
576     up = White, down = Grey;
577     if (ishighlighted(c))
578 	up = Grey, down = LightGrey;
579     else if (hasfocus(c)) {
580 	setcolour(Black);
581 	drawrect(r);
582 	r = insetr(r,1);
583     }
584     draw_shadow(r, up, down, SHADOW_WIDTH);
585 
586     setcolour(old);
587 }
588 
newbutton(const char * text,rect r,actionfn fn)589 button newbutton(const char *text, rect r, actionfn fn)
590 {
591     button obj = newcontrol(text, r);
592     if (obj) {
593 	obj->kind = ButtonObject;
594 	setredraw(obj, draw_button);
595 	setmousedown(obj, button_mousedown);
596 	setmousemove(obj, button_mousemove);
597 	setmousedrag(obj, button_mousemove);
598 	setmouseup(obj, button_mouseup);
599 	setkeydown(obj, button_keydown);
600 	setaction(obj, fn);
601 	setbackground(obj, LightGrey);
602 	settextfont(obj, SystemFont);
603 	show(obj);
604     }
605     return obj;
606 }
607 #endif
608 
609 #if USE_NATIVE_TOGGLES
newcheckbox(const char * text,rect r,actionfn fn)610 checkbox newcheckbox(const char *text, rect r, actionfn fn)
611 {
612     checkbox obj = newchildwin("button", text, BS_CHECKBOX, r, fn);
613     if (obj) {
614 	obj->kind = CheckboxObject;
615 	setbackground(obj, getbackground(parentwindow(obj)));
616     }
617     return obj;
618 }
619 
newradiobutton(const char * text,rect r,actionfn fn)620 radiobutton newradiobutton(const char *text, rect r, actionfn fn)
621 {
622     radiobutton obj = newchildwin("button", text, BS_RADIOBUTTON, r, fn);
623     if (obj) {
624 	obj->kind = RadioObject;
625 	setbackground(obj, getbackground(parentwindow(obj)));
626     }
627     return obj;
628 }
629 #else
draw_checkmark(rect r)630 static void draw_checkmark(rect r)
631 {
632     int len;
633 
634     if (r.width < 8) {
635 	r = insetr(r,1);
636 	fillrect(r);
637 	return;
638     }
639 
640     len = r.width/3;
641     if (len < 3)
642 	len = 3;
643 
644     drawline(pt(r.x+1, r.y+r.height-len-2),
645 	     pt(r.x+len, r.y+r.height-3));
646     drawline(pt(r.x+2, r.y+r.height-len-2),
647 	     pt(r.x+len, r.y+r.height-4));
648     drawline(pt(r.x+len, r.y+r.height-3),
649 	     pt(r.x+r.width-1, r.y+len-2));
650     drawline(pt(r.x+len, r.y+r.height-4),
651 	     pt(r.x+r.width-2, r.y+len-2));
652 }
653 
draw_checkbox(control c,rect r)654 static void draw_checkbox(control c, rect r)
655 {
656     int w;
657     rect box, textrect;
658     char *name;
659     int style = (AlignLeft | AlignTop);
660     font f;
661     rgb old = currentcolour();
662 
663     /* Calculate rectangles. */
664     f = gettextfont(c);
665     setfont(f);
666     w = strwidth(f,"W");
667     if (w > r.width)  w = r.width;
668     if (w > r.height) w = r.height;
669     box = rect(r.x,r.y+1,w,w);
670     if (w < getheight(f) - getdescent(f))
671 	box.y += getheight(f) - getdescent(f) - w;
672     textrect = rect(r.x+w+w/2,r.y,r.width-(w+w/2),r.height);
673 
674     /* Clear check area. */
675     setlinewidth(1);
676     setcolour(White);
677     fillrect(insetr(box,1));
678 
679     /* Draw check area */
680     if (isenabled(c))
681 	setcolour(Black);
682     else
683 	setcolour(Grey);
684     drawrect(box);
685 
686     /* 'Pressed button' effect by black border in box. */
687     if (ishighlighted(c))
688 	drawrect(insetr(box,1));
689 
690     /* Put tick in box if checked. */
691     if (ischecked(c))
692 	draw_checkmark(insetr(box,1));
693 
694     name = getname(c);
695     if (isenabled(c)) {
696 	/* if (hasfocus(c)) {
697 	   style |= Underline;
698 	   setlinewidth(2);
699 	   } */
700 	setcolour(getforeground(c));
701     }
702     drawtext(textrect, style, name);
703 
704     setcolour(old);
705 }
706 
newcheckbox(const char * text,rect r,actionfn fn)707 checkbox newcheckbox(const char *text, rect r, actionfn fn)
708 {
709     checkbox obj = newcontrol(text, r);
710     if (obj) {
711 	obj->kind = CheckboxObject;
712 	setredraw(obj, draw_checkbox);
713 	setmousedown(obj, checkbox_mousedown);
714 	setmousemove(obj, checkbox_mousemove);
715 	setmousedrag(obj, checkbox_mousemove);
716 	setmouseup(obj, checkbox_mouseup);
717 	setkeydown(obj, checkbox_keydown);
718 	setaction(obj, fn);
719 	setbackground(obj, getbackground(parentwindow(obj)));
720 	settextfont(obj, SystemFont);
721 	show(obj);
722     }
723     return obj;
724 }
725 
draw_radio(control c,rect r)726 static void draw_radio(control c, rect r)
727 {
728     int w;
729     rect box, textrect;
730     char *name;
731     int style = (AlignLeft | AlignTop);
732     font f;
733     rgb old = currentcolour();
734 
735     /* Calculate rectangles. */
736     f = gettextfont(c);
737     setfont(f);
738     w = strwidth(f,"W");
739     if (w > r.width)  w = r.width;
740     if (w > r.height) w = r.height;
741     box = rect(r.x,r.y+1,w,w);
742     if (w < getheight(f) - getdescent(f))
743 	box.y += getheight(f) - getdescent(f) - w;
744     textrect = rect(r.x+w+w/2,r.y,r.width-(w+w/2),r.height);
745 
746     /* Clear the check area. */
747     setlinewidth(1);
748     setcolour(White);
749     fillellipse(insetr(box,1));
750 
751     /* Draw the check area */
752     if (isenabled(c))
753 	setcolour(Black);
754     else
755 	setcolour(Grey);
756     drawellipse(box);
757 
758     /* Provide 'pressed button' effect by black border. */
759     if (ishighlighted(c)) {
760 	setlinewidth(2);
761 	drawellipse(box);
762 	setlinewidth(1);
763     }
764 
765     /* Put o in circle if checked. */
766     if (ischecked(c))
767 	fillellipse(insetr(box,3));
768 
769     name = getname(c);
770     if (isenabled(c)) {
771 	/* if (hasfocus(c)) {
772 	   style |= Underline;
773 	   setlinewidth(2);
774 	   } */
775 	setcolour(getforeground(c));
776     }
777     drawtext(textrect, style, name);
778 
779     setcolour(old);
780 }
781 
radio_hit(control c)782 static void radio_hit(control c)
783 {
784     if (!ischecked(c)) {
785 	uncheck_neighbours(c);
786 	check(c);
787 	activatecontrol(c);
788     }
789 }
790 
radio_mouseup(control c,int buttons,point xy)791 static void radio_mouseup(control c, int buttons, point xy)
792 {
793     if (! isarmed(c))
794 	return;
795     disarm(c);
796     unhighlight(c);
797     if (! ptinr(xy, getrect(c)))
798 	return;
799     radio_hit(c);
800 }
801 
radio_keydown(control c,int ch)802 static void radio_keydown(control c, int ch)
803 {
804     if (ch == ' ')
805 	radio_hit(c);
806 }
807 
newradiobutton(const char * text,rect r,actionfn fn)808 radiobutton newradiobutton(const char *text, rect r, actionfn fn)
809 {
810     radiobutton obj = newcontrol(text, r);
811     if (obj) {
812 	obj->kind = RadioObject;
813 	setredraw(obj, draw_radio);
814 	setmousedown(obj, checkbox_mousedown);
815 	setmousemove(obj, checkbox_mousemove);
816 	setmousedrag(obj, checkbox_mousemove);
817 	setmouseup(obj, radio_mouseup);
818 	setkeydown(obj, radio_keydown);
819 	setaction(obj, fn);
820 	setbackground(obj, getbackground(parentwindow(obj)));
821 	settextfont(obj, SystemFont);
822 	show(obj);
823     }
824     return obj;
825 }
826 #endif
827 
undotext(control obj)828 void undotext(control obj)  /* Why was this previously commented out? CJ */
829 {
830     if (! obj)
831 	return;
832     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
833 	return;
834     sendmessage(obj->handle, EM_UNDO, 0, 0L);
835 }
836 
cuttext(control obj)837 void cuttext(control obj)
838 {
839     if (! obj)
840 	return;
841     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
842 	return;
843     sendmessage(obj->handle, WM_CUT, 0, 0L);
844 }
845 
copytext(control obj)846 void copytext(control obj)
847 {
848     if (! obj)
849 	return;
850     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
851 	return;
852     sendmessage(obj->handle, WM_COPY, 0, 0L);
853 }
854 
cleartext(control obj)855 void cleartext(control obj)
856 {
857     if (! obj)
858 	return;
859     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
860 	return;
861     sendmessage(obj->handle, WM_CLEAR, 0, 0L);
862 }
863 
pastetext(control obj)864 void pastetext(control obj)
865 {
866     if (! obj)
867 	return;
868     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
869 	return;
870     sendmessage(obj->handle, WM_PASTE, 0, 0L);
871 }
872 
inserttext(control obj,const char * text)873 void inserttext(control obj, const char *text)
874 {
875     if (! obj)
876 	return;
877     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
878 	return;
879     text = to_dos_string(text);
880     sendmessage(obj->handle, EM_REPLACESEL, 0, (intptr_t) text);
881     if (text)
882 	discard(text);
883 }
884 
selecttext(control obj,long start,long end)885 void selecttext(control obj, long start, long end)
886 {
887     int left, right;
888     long length;
889 
890     if (! obj)
891 	return;
892     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
893 	return;
894     length = GetWindowTextLength(obj->handle);
895     left = (start < 0) ? length : start;
896     right = (end < 0) ? length : end;
897 #ifdef WIN32
898     sendmessage(obj->handle, EM_SETSEL, left,right);
899 #else
900     sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(left,right));
901 #endif
902 }
903 
textselection(control obj,long * start,long * end)904 void textselection(control obj, long *start, long *end)
905 {
906     unsigned long sel;
907 
908     if (! obj)
909 	return;
910     if ((obj->kind != FieldObject) && (obj->kind != TextboxObject))
911 	return;
912     sel = sendmessage(obj->handle, EM_GETSEL, 0, 0);
913     if (start) *start = LOWORD(sel);
914     if (end) *end = HIWORD(sel);
915 }
916 
917 
newfield(const char * text,rect r)918 field newfield(const char *text, rect r)
919 {
920     field obj = newchildwin("edit", NULL,
921 			    WS_BORDER | ES_LEFT | ES_AUTOHSCROLL,
922 			    r, NULL);
923     if (obj) {
924 	obj->kind = FieldObject;
925 	settext(obj, text);
926     }
927     return obj;
928 }
929 
930 
newfield_no_border(const char * text,rect r)931 field newfield_no_border(const char *text, rect r)
932 {
933     field obj = newchildwin("edit", NULL,
934 			    ES_LEFT | ES_AUTOHSCROLL,
935 			    r, NULL);
936     if (obj) {
937 	obj->kind = FieldObject;
938 	settext(obj, text);
939     }
940     return obj;
941 }
942 
newpassword(const char * text,rect r)943 field newpassword(const char *text, rect r)
944 {
945     field obj = newchildwin("edit", NULL,
946 			    WS_BORDER | ES_LEFT | ES_AUTOHSCROLL
947 			    | ES_PASSWORD, r, NULL);
948     if (obj) {
949 	obj->kind = FieldObject;
950 	settextfont(obj, SystemFont);
951 	settext(obj, text);
952     }
953     return obj;
954 }
955 
newtextbox(const char * text,rect r)956 textbox newtextbox(const char *text, rect r)
957 {
958     textbox obj = newchildwin("edit", NULL,
959 			      /* WS_HSCROLL | ES_AUTOHSCROLL | */
960 			      WS_VSCROLL | ES_AUTOVSCROLL |
961 			      WS_BORDER | ES_LEFT |
962 			      ES_MULTILINE,
963 			      r, NULL);
964     if (obj) {
965 	obj->kind = TextboxObject;
966 	settext(obj, text);
967     }
968     return obj;
969 }
970 
newtextarea(const char * text,rect r)971 textbox newtextarea(const char *text, rect r)
972 {
973     textbox obj = newchildwin("edit", NULL,
974 			      WS_HSCROLL | ES_AUTOHSCROLL |
975 			      WS_VSCROLL | ES_AUTOVSCROLL |
976 			      WS_BORDER | ES_LEFT |
977 			      ES_MULTILINE,
978 			      r, NULL);
979     if (obj) {
980 	obj->kind = TextboxObject;
981 	settext(obj, text);
982     }
983     return obj;
984 }
985 
newrichtextarea(const char * text,rect r)986 textbox newrichtextarea(const char *text, rect r)
987 {
988     textbox obj;
989     if (!LoadLibrary("riched20.dll")) /* RichEdit version 2.0, not included in Win95 */
990 	LoadLibrary("riched32.dll");  /* RichEdit version 1.0 */
991     obj = newchildwin(RICHEDIT_CLASS, NULL,
992 		      WS_HSCROLL | ES_AUTOHSCROLL |
993 		      WS_VSCROLL | ES_AUTOVSCROLL |
994 		      ES_LEFT | ES_MULTILINE | ES_NOHIDESEL,
995 		      r, NULL);
996     if (obj) {
997 	sendmessage(obj->handle, EM_SETTEXTMODE, TM_PLAINTEXT, 0);
998 	obj->kind = TextboxObject;
999 	settext(obj, text);
1000     }
1001     return obj;
1002 }
1003 
1004 static SCROLLINFO si;
1005 
newscrollbar(rect r,int max,int pagesize,scrollfn fn)1006 scrollbar newscrollbar(rect r, int max, int pagesize, scrollfn fn)
1007 {
1008     scrollbar obj;
1009     HWND hwnd;
1010 
1011     r = rcanon(r);
1012 
1013     obj = newchildwin("scrollbar", NULL,
1014 		      (r.width > r.height) ? SBS_HORZ : SBS_VERT,
1015 		      r, NULL);
1016     if (obj) {
1017 	obj->kind = ScrollbarObject;
1018 	obj->hit = fn;
1019 	obj->value = 0;
1020 	obj->max = max;
1021 	obj->size = pagesize;
1022 
1023 	hwnd = obj->handle;
1024 	si.cbSize = sizeof(si);
1025 	si.fMask = SIF_ALL;
1026 	si.nMin = 0;
1027 	si.nMax = max + pagesize - 1;
1028 	si.nPage = pagesize;
1029 	si.nPos = 0;
1030 	SetScrollInfo(hwnd, SB_CTL, &si, 1);
1031     }
1032     return obj;
1033 }
1034 
changescrollbar(scrollbar obj,int where,int max,int pagesize)1035 void changescrollbar(scrollbar obj, int where, int max, int pagesize)
1036 {
1037     HWND hwnd;
1038 
1039     if (! obj)
1040 	return;
1041     hwnd = obj->handle;
1042     obj->max = max;
1043     obj->size = pagesize;
1044 
1045     si.cbSize = sizeof(si);
1046     si.fMask = SIF_ALL;
1047     si.nMin = 0;
1048     si.nMax = max + pagesize - 1;
1049     si.nPage = pagesize;
1050     si.nPos = where;
1051     SetScrollInfo(hwnd, SB_CTL, &si, 1);
1052 }
1053 
newlistbox(const char * list[],rect r,scrollfn fn,actionfn dble)1054 listbox newlistbox(const char *list[], rect r, scrollfn fn, actionfn dble)
1055 {
1056     listbox obj;
1057 
1058     obj = newchildwin("listbox", NULL,
1059 		      LBS_NOTIFY | WS_BORDER |
1060 		      WS_VSCROLL | WS_HSCROLL,
1061 		      r, NULL);
1062     if (! obj)
1063 	return obj;
1064     obj->kind = ListboxObject;
1065     obj->hit = fn;
1066     obj->dble = dble;
1067 
1068     changelistbox(obj, list);
1069 
1070     return obj;
1071 }
1072 
newmultilist(const char * list[],rect r,scrollfn fn,actionfn dble)1073 listbox newmultilist(const char *list[], rect r, scrollfn fn, actionfn dble)
1074 {
1075     listbox obj;
1076 
1077     obj = newchildwin("listbox", NULL,
1078 		      LBS_NOTIFY |
1079 		      LBS_MULTIPLESEL | LBS_EXTENDEDSEL |
1080 		      WS_BORDER |
1081 		      WS_VSCROLL | WS_HSCROLL,
1082 		      r, NULL);
1083     if (! obj)
1084 	return obj;
1085     obj->kind = MultilistObject;
1086     obj->hit = fn;
1087     obj->dble = dble;
1088 
1089     changelistbox(obj, list);
1090 
1091     return obj;
1092 }
1093 
newdroplist(const char * list[],rect r,scrollfn fn)1094 listbox newdroplist(const char *list[], rect r, scrollfn fn)
1095 {
1096     listbox obj;
1097     int h, i;
1098 
1099     initapp(0,0);
1100     h = getheight(SystemFont);
1101     r.height = h+h;
1102     for (i = 0; list && list[i]; i++)
1103 	r.height += h;
1104 
1105     obj = newchildwin("combobox", NULL,
1106 		      CBS_DROPDOWNLIST | CBS_AUTOHSCROLL |
1107 		      //CBS_DISABLENOSCROLL |
1108 		      WS_BORDER |
1109 		      WS_VSCROLL | WS_HSCROLL,
1110 		      r, NULL);
1111     if (! obj)
1112 	return obj;
1113     obj->kind = DroplistObject;
1114     obj->hit = fn;
1115 
1116     changelistbox(obj, list);
1117 
1118     return obj;
1119 }
1120 
newdropfield(const char * list[],rect r,scrollfn fn)1121 listbox newdropfield(const char *list[], rect r, scrollfn fn)
1122 {
1123     listbox obj;
1124     int h, i;
1125 
1126     initapp(0,0);
1127     h = getheight(SystemFont);
1128     r.height = h+h;
1129     for (i = 0; list && list[i]; i++)
1130 	r.height += h;
1131 
1132     obj = newchildwin("combobox", NULL,
1133 		      CBS_DROPDOWN | CBS_AUTOHSCROLL |
1134 		      // CBS_DISABLENOSCROLL |
1135 		      WS_BORDER |
1136 		      WS_VSCROLL | WS_HSCROLL,
1137 		      r, NULL);
1138     if (! obj)
1139 	return obj;
1140     obj->kind = DropfieldObject;
1141     obj->hit = fn;
1142 
1143     changelistbox(obj, list);
1144 
1145     return obj;
1146 }
1147 
setlistitem(listbox obj,int index)1148 void setlistitem(listbox obj, int index)
1149 {
1150     int count;
1151 
1152     if (! obj)
1153 	return;
1154     if (index < 0)
1155 	index = -1;
1156     switch (obj->kind)
1157     {
1158     case ListboxObject:
1159 	sendmessage(obj->handle, LB_SETCURSEL, index, 0L);
1160 	break;
1161     case MultilistObject:
1162 	if (index >= 0)
1163 	    sendmessage(obj->handle, LB_SETSEL, TRUE, MAKELPARAM(index, 0));
1164 	else {
1165 	    count = sendmessage(obj->handle, LB_GETCOUNT, 0, 0L);
1166 	    sendmessage(obj->handle, LB_SELITEMRANGE, FALSE, MAKELPARAM(0,count-1));
1167 	}
1168     case DroplistObject:
1169     case DropfieldObject:
1170 	sendmessage(obj->handle, CB_SETCURSEL, index, 0L);
1171 	break;
1172     default:
1173 	break;
1174     }
1175 }
1176 
isselected(listbox obj,int index)1177 int isselected(listbox obj, int index)
1178 {
1179     if (! obj)
1180 	return -1;
1181     switch (obj->kind)
1182     {
1183     case ListboxObject:
1184 	return (index == sendmessage(obj->handle, LB_GETCURSEL, 0, 0L));
1185     case MultilistObject:
1186 	return sendmessage(obj->handle, LB_GETSEL, index, 0L);
1187     case DroplistObject:
1188     case DropfieldObject:
1189 	return (index == sendmessage(obj->handle, CB_GETCURSEL, 0, 0L));
1190     default:
1191 	return 0;
1192     }
1193 }
1194 
getlistitem(listbox obj)1195 int getlistitem(listbox obj)
1196 {
1197     int index, count;
1198 
1199     if (! obj)
1200 	return -1;
1201     switch (obj->kind)
1202     {
1203     case ListboxObject:
1204 	return sendmessage(obj->handle, LB_GETCURSEL, 0, 0L);
1205     case MultilistObject:
1206 	count = sendmessage(obj->handle, LB_GETCOUNT, 0, 0L);
1207 	for (index=0; index < count; index++)
1208 	    if (isselected(obj, index))
1209 		return index;
1210 	return -1;
1211     case DroplistObject:
1212     case DropfieldObject:
1213 	return sendmessage(obj->handle, CB_GETCURSEL, 0, 0L);
1214     default:
1215 	return -1;
1216     }
1217 }
1218 
changelistbox(listbox obj,const char ** list)1219 void changelistbox(listbox obj, const char **list)
1220 {
1221     int i;
1222     HWND hwnd;
1223     UINT reset_msg, add_msg;
1224 
1225     if (! obj)
1226 	return;
1227     hwnd = obj->handle;
1228 
1229     switch (obj->kind) {
1230     case ListboxObject: case MultilistObject:
1231 	reset_msg = LB_RESETCONTENT;
1232 	add_msg = LB_ADDSTRING;
1233 	break;
1234     case DroplistObject: case DropfieldObject:
1235 	reset_msg = CB_RESETCONTENT;
1236 	add_msg = CB_ADDSTRING;
1237 	break;
1238     default:
1239 	return;
1240     }
1241 
1242     sendmessage(hwnd, WM_SETREDRAW, FALSE, 0L);
1243     sendmessage(hwnd, reset_msg, 0, 0L);
1244     for (i=0; list && list[i]; i++)
1245 	sendmessage(hwnd, add_msg, 0, (LPSTR) list[i]);
1246     sendmessage(hwnd, WM_SETREDRAW, TRUE, 0L);
1247     if (obj->kind == ListboxObject)
1248 	sendmessage(hwnd, LB_SETCURSEL, 0, 0L);
1249 }
1250 
1251 /*
1252  *  Activate a control's action function. We do several things here.
1253  *  Checking to see what kind of control it is, we handle some
1254  *  events and discard others. We automatically toggle the state of
1255  *  checkboxes and radio buttons, and handle listbox changes and
1256  *  allow text box update events to call the control's action.
1257  */
1258 PROTECTED
handle_control(HWND hwnd,UINT message)1259 void handle_control(HWND hwnd, UINT message)
1260 {
1261     object obj;
1262     int index;
1263 
1264     obj = find_by_handle(hwnd);
1265 
1266     if ((! obj) || (! (obj->state & GA_Enabled)))
1267 	return;
1268 
1269     /* Only let certain events cause activation. */
1270     switch (obj->kind)
1271     {
1272     case CheckboxObject:
1273 	if (obj->state & GA_Checked)
1274 	    uncheck(obj);
1275 	else
1276 	    check(obj);
1277 	break;
1278 
1279     case RadioObject:
1280 	if (!(obj->state & GA_Checked)) {
1281 	    uncheck_neighbours(obj);
1282 	    check(obj);
1283 	}
1284 	break;
1285 
1286     case ListboxObject:
1287 	if (message == LBN_DBLCLK) {
1288 	    if(obj->dble) obj->dble(obj);
1289 	    return;
1290 	}
1291 	/* Ignore all but selection-change events. */
1292 	if (message != LBN_SELCHANGE) return;
1293 
1294 	index = sendmessage(hwnd, LB_GETCURSEL, 0, 0L);
1295 	obj->value = index;
1296 	break;
1297 
1298     case MultilistObject:
1299 	if (message == LBN_DBLCLK) {
1300 	    if(obj->dble) obj->dble(obj);
1301 	    return;
1302 	}
1303 	/* Ignore all but selection-change events. */
1304 	if (message != LBN_SELCHANGE)
1305 	    return;
1306 	index = sendmessage(hwnd, LB_GETCARETINDEX, 0, 0L);
1307 	/* We do want to see de-selection events too
1308 	   if (! sendmessage(hwnd, LB_GETSEL, index, 0L))
1309 	   return;*/
1310 	obj->value = index;
1311 	break;
1312 
1313     case DroplistObject:
1314     case DropfieldObject:
1315 	/* Ignore all but selection-change events. */
1316 	if (message != CBN_SELCHANGE)
1317 	    return;
1318 	index = sendmessage(hwnd, CB_GETCURSEL, 0, 0L);
1319 	obj->value = index;
1320 	break;
1321 
1322     case FieldObject:
1323     case TextboxObject:
1324 	if (message == EN_MAXTEXT) {
1325 	    /* increase the character limit in the editor by 50%
1326 	       if the limit is reached, but this should not
1327 	       happen */
1328 	    setlimittext(obj, 1.5 * getlimittext(obj));
1329 	}
1330 	/* Ignore everything else but killfocus. */
1331 	else if (message != EN_KILLFOCUS)
1332 	    return;
1333 	break;
1334 
1335     default:
1336 	break;
1337     }
1338     /* activate the control's callback */
1339     activatecontrol(obj);
1340 }
1341 
1342 #include <commctrl.h>
1343 /* smooth  != 0 gives continuous not segmented bar */
newprogressbar(rect r,int pbmin,int pbmax,int incr,int smooth)1344 progressbar newprogressbar(rect r, int pbmin, int pbmax, int incr, int smooth)
1345 {
1346     HWND hwnd;
1347     progressbar obj;
1348     int sm;
1349 
1350     ensure_window();
1351     r = rcanon(r);
1352     sm = smooth ? PBS_SMOOTH : 0 ;
1353     hwnd = CreateWindowEx(0, PROGRESS_CLASS, NULL,
1354 			  (WS_CHILD | WS_VISIBLE | sm),
1355 			  r.x, r.y, r.width, r.height,
1356 			  current_window->handle,
1357 			  (HMENU) child_id, this_instance, NULL);
1358     obj = new_object(ControlObject, hwnd, current_window);
1359     if (! obj) {
1360 	DestroyWindow(hwnd);
1361 	return NULL;
1362     }
1363     obj->die = private_delcontrol;
1364     obj->rect = r;
1365     obj->id = child_id++;
1366     obj->action = NULL;
1367     obj->state = (GA_Visible | GA_Enabled);
1368     obj->flags = ChildWindow;
1369     set_new_winproc(obj); /* set custom winproc */
1370     settextfont(obj, SystemFont);
1371     obj->kind = ListboxObject;
1372     SendMessage(hwnd, PBM_SETRANGE32, (WPARAM) pbmin, (LPARAM) pbmax);
1373     SendMessage(hwnd, PBM_SETSTEP, (WPARAM) incr, 0);
1374 
1375     return obj;
1376 }
1377 
setprogressbar(progressbar obj,int n)1378 void setprogressbar(progressbar obj, int n)
1379 {
1380     if (! obj) return;
1381     SendMessage(obj->handle, PBM_SETPOS, (WPARAM) n, 0);
1382 }
1383 
stepprogressbar(progressbar obj,int n)1384 void stepprogressbar(progressbar obj, int n)
1385 {
1386     if (! obj) return;
1387     SendMessage(obj->handle, PBM_STEPIT, 0, 0);
1388 }
1389 
setprogressbarrange(progressbar obj,int pbmin,int pbmax)1390 void setprogressbarrange(progressbar obj, int pbmin, int pbmax)
1391 {
1392     if (! obj) return;
1393     SendMessage(obj->handle, PBM_SETRANGE32, (WPARAM) pbmin,
1394 		(LPARAM) pbmax);
1395 }
1396