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