1 /*
2  * GraphApp - Cross-Platform Graphics Programming Library.
3  *
4  * File: controls.c -- manipulating scrollbars, buttons, etc.
5  * Platform: Windows  Version: 2.40  Date: 1998/04/04
6  *
7  * Version: 1.00  Changes: Original version by Lachlan Patrick.
8  * Version: 1.60  Changes: Added clear(), draw(), flashcontrol().
9  * Version: 2.00  Changes: New object class system.
10  * Version: 2.15  Changes: Transparent backgrounds added.
11  * Version: 2.20  Changes: Non-native buttons supported.
12  * Version: 2.35  Changes: New reference count technique.
13  * Version: 2.40  Changes: Support for new controls.
14  */
15 
16 /* Copyright (C) 1993-1998 Lachlan Patrick
17 
18    This file is part of GraphApp, a cross-platform C graphics library.
19 
20    GraphApp is free software; you can redistribute it and/or modify it
21    under the terms of the GNU Library General Public License.
22    GraphApp is distributed in the hope that it will be useful, but
23    WITHOUT ANY WARRANTY.
24 
25    See the file COPYLIB.TXT for details.
26 */
27 
28 /*  Copyright (C) 2004	The R Foundation
29 
30     Changes for R:
31 
32     sort out resize (confused screen and client coords)
33     add printer and metafile handling
34     Remove assumption of current->dest being non-NULL
35 
36  */
37 
38 #include "internal.h"
39 #include <richedit.h>
40 
41 # define alloca(x) __builtin_alloca((x))
42 
43 /*
44  *  Setting control call-backs.
45  */
setaction(control obj,actionfn fn)46 void setaction(control obj, actionfn fn)
47 {
48     if (obj)
49 	obj->action = fn;
50 }
51 
sethit(control obj,intfn fn)52 void sethit(control obj, intfn fn)
53 {
54     if (obj)
55 	obj->hit = fn;
56 }
57 
setdel(control obj,actionfn fn)58 void setdel(control obj, actionfn fn)
59 {
60     if (obj)
61 	if (obj->call)
62 	    obj->call->die = fn;
63 }
64 
setclose(control obj,actionfn fn)65 void setclose(control obj, actionfn fn)
66 {
67     if (obj)
68 	if (obj->call)
69 	    obj->call->close = fn;
70 }
71 
setredraw(control obj,drawfn fn)72 void setredraw(control obj, drawfn fn)
73 {
74     if (obj)
75 	if (obj->call)
76 	    obj->call->redraw = fn;
77 }
78 
setresize(control obj,drawfn fn)79 void setresize(control obj, drawfn fn)
80 {
81     if (obj)
82 	if (obj->call)
83 	    obj->call->resize = fn;
84 }
85 
setkeydown(control obj,keyfn fn)86 void setkeydown(control obj, keyfn fn)
87 {
88     if (obj)
89 	if (obj->call)
90 	    obj->call->keydown = fn;
91 }
92 
setkeyaction(control obj,keyfn fn)93 void setkeyaction(control obj, keyfn fn)
94 {
95     if (obj)
96 	if (obj->call)
97 	    obj->call->keyaction = fn;
98 }
99 
setmousedown(control obj,mousefn fn)100 void setmousedown(control obj, mousefn fn)
101 {
102     if (obj)
103 	if (obj->call)
104 	    obj->call->mousedown = fn;
105 }
106 
setmouseup(control obj,mousefn fn)107 void setmouseup(control obj, mousefn fn)
108 {
109     if (obj)
110 	if (obj->call)
111 	    obj->call->mouseup = fn;
112 }
113 
setmousemove(control obj,mousefn fn)114 void setmousemove(control obj, mousefn fn)
115 {
116     if (obj)
117 	if (obj->call)
118 	    obj->call->mousemove = fn;
119 }
120 
setmousedrag(control obj,mousefn fn)121 void setmousedrag(control obj, mousefn fn)
122 {
123     if (obj)
124 	if (obj->call)
125 	    obj->call->mousedrag = fn;
126 }
127 
setmouserepeat(control obj,mousefn fn)128 void setmouserepeat(control obj, mousefn fn)
129 {
130     if (obj)
131 	if (obj->call)
132 	    obj->call->mouserepeat = fn;
133 }
134 
setdrop(control obj,dropfn fn)135 void setdrop(control obj, dropfn fn)
136 {
137     if (obj)
138 	if (obj->call) {
139 	    DragAcceptFiles(obj->handle, TRUE);
140 	    obj->call->drop = fn;
141 	}
142 }
143 
setonfocus(control obj,actionfn fn)144 void setonfocus(control obj, actionfn fn)
145 {
146     if (obj)
147 	obj->call->focus = fn;
148 }
149 
setim(control obj,imfn fn)150 void setim(control obj, imfn fn)
151 {
152     if (obj)
153 	obj->call->im = fn;
154 }
155 
156 /*
157  *  Drawing controls and windows.
158  */
clear(control obj)159 void clear(control obj)
160 {
161     drawing prev;
162     rgb old;
163 
164     if (! obj)
165 	return;
166     if (! isvisible(obj))
167 	return;
168     if (! isvisible(parentwindow(obj)))
169 	return;
170     if (obj->bg == Transparent)
171 	return;
172     prev = current->dest;
173     drawto(obj);
174     old = currentrgb();
175     setrgb(obj->bg);
176     fillrect(getrect(obj));
177     if (prev) {
178 	setrgb(old);
179 	drawto(prev);
180     }
181 }
182 
draw(control obj)183 void draw(control obj)
184 {
185     drawing prev;
186     drawstate old = NULL;
187 
188     if (! obj)
189 	return;
190     if (obj->kind == MenubarObject) {
191 	DrawMenuBar(obj->parent->handle);
192 	return;
193     }
194 
195     if (! isvisible(obj))
196 	return;
197     if (! isvisible(parentwindow(obj)))
198 	return;
199     if ((obj->call == NULL) || (obj->call->redraw == NULL))
200 	return;
201     prev = current->dest;
202     drawto(obj);
203     if (prev) old = copydrawstate();
204     moveto(pt(0,0));
205     obj->call->redraw(obj, getrect(obj));
206     if (prev) {
207 	restoredrawstate(old);
208 	drawto(prev);
209     }
210 }
211 
redraw(control obj)212 void redraw(control obj)
213 {
214     clear(obj);
215     draw(obj);
216 }
217 
218 /*  void getscreenrect(control obj, rect *r) */
219 /*  { */
220 /*      RECT W; */
221 /*      GetWindowRect(obj->handle, &W); */
222 /*      r->x = W.left; */
223 /*      r->y = W.top; */
224 /*      r->width = W.right - W.left; */
225 /*      r->height = W.bottom - W.top; */
226 /*  } */
227 
228 
229 /* The original here used GetWindowRect (which used screen coordinates)
230    and MoveWindow (which uses client coordinates) so got the positioning
231    hopelessly wrong.  This version works for WindowObjects, but I would be
232    suspicious of it for other cases.  BDR 2000/04/05
233 */
resize(control obj,rect r)234 void resize(control obj, rect r)
235 {
236     RECT R;
237     WINDOWPLACEMENT W;
238     int dw, dh, dx, dy;
239 
240     if (! obj)
241 	return;
242     r = rcanon(r);
243     if (obj->kind == WindowObject) {
244 	W.length = sizeof(WINDOWPLACEMENT);
245 	r.x = obj->rect.x;
246 	r.y = obj->rect.y;
247 	if (!equalr(r, obj->rect)) {
248 	    GetWindowPlacement(obj->handle, &W);
249 	    if (!isvisible(obj)) W.showCmd = SW_HIDE;  /* stops the resize from revealing the window */
250 	    dx = r.x - obj->rect.x;
251 	    dy = r.y - obj->rect.y;
252 	    /* don't believe current sizes!
253 	       dw = r.width - obj->rect.width;
254 	       dh = r.height - obj->rect.height;
255 	       Rprintf("dw %d dh %d\n", dw, dh); */
256 	    GetClientRect(obj->handle, &R);
257 	    dw = r.width - (R.right - R.left);
258 	    dh = r.height - (R.bottom - R.top);
259 	    W.rcNormalPosition.left += dx;
260 	    W.rcNormalPosition.top += dy;
261 	    W.rcNormalPosition.right += dx + dw;
262 	    W.rcNormalPosition.bottom += dy + dh;
263 	    SetWindowPlacement(obj->handle, &W);
264 	}
265     }
266     else {
267 	if (! equalr(r, obj->rect))
268 	    MoveWindow(obj->handle, r.x, r.y, r.width, r.height, 1);
269 	obj->rect.x = r.x;
270 	obj->rect.y = r.y;
271     }
272     obj->rect.width = r.width;
273     obj->rect.height = r.height;
274 }
275 
276 /*
277  *  Showing and hiding controls and windows.
278  */
show(control obj)279 void show(control obj)
280 {
281     if (! obj)
282 	return;
283     switch (obj->kind) {
284     case CursorObject: case FontObject: case BitmapObject:
285     case MenubarObject: case MenuObject: case MenuitemObject:
286 	break;
287     case WindowObject:
288 	obj->state |= GA_Visible;
289 	show_window(obj);
290 	break;
291     default:
292 	ShowWindow(obj->handle, SW_SHOWNORMAL);
293 	SetFocus(obj->handle);
294 	UpdateWindow(obj->handle);
295     }
296     obj->state |= GA_Visible;
297 }
298 
hide(control obj)299 void hide(control obj)
300 {
301     if (! obj)
302 	return;
303     switch (obj->kind) {
304     case CursorObject: case FontObject: case BitmapObject:
305     case MenubarObject: case MenuObject: case MenuitemObject:
306 	break;
307     case WindowObject:
308 	hide_window(obj);
309 	break;
310     default:
311 	ShowWindow(obj->handle, SW_HIDE);
312     }
313     obj->state &= ~GA_Visible;
314 }
315 
isvisible(control obj)316 int isvisible(control obj)
317 {
318     if (! obj)
319 	return 0;
320     return (obj->state & GA_Visible) ? 1 : 0;
321 }
322 
323 /*
324  *  Enabling and disabling controls and windows.
325  */
enable(control obj)326 void enable(control obj)
327 {
328     if (! obj)
329 	return;
330     switch (obj->kind) {
331     case CursorObject: case FontObject: case BitmapObject:
332     case MenubarObject:
333 	break;
334     case MenuObject: case MenuitemObject:
335 	EnableMenuItem(obj->parent->handle,
336 		       obj->id, MF_ENABLED | MF_BYCOMMAND);
337 	break;
338     case FieldObject: case TextboxObject:
339 	sendmessage(obj->handle, EM_SETREADONLY, FALSE, 0L);
340 	break;
341     default:
342 	if (! isenabled(obj)) {
343 	    EnableWindow(obj->handle, 1);
344 	    obj->state |= GA_Enabled;
345 	    draw(obj);
346 	}
347     }
348     obj->state |= GA_Enabled;
349 }
350 
disable(control obj)351 void disable(control obj)
352 {
353     if (! obj)
354 	return;
355     switch (obj->kind) {
356     case CursorObject: case FontObject: case BitmapObject:
357     case MenubarObject: case MenuObject:
358 	break;
359     case MenuitemObject:
360 	EnableMenuItem(obj->parent->handle,
361 		       obj->id, MF_GRAYED | MF_BYCOMMAND);
362 	break;
363     case FieldObject: case TextboxObject:
364 	sendmessage(obj->handle, EM_SETREADONLY, TRUE, 0L);
365 	break;
366     default:
367 	if (isenabled(obj)) {
368 	    EnableWindow(obj->handle, 0);
369 	    obj->state &= ~GA_Enabled;
370 	    draw(obj);
371 	}
372     }
373     obj->state &= ~GA_Enabled;
374 }
375 
isenabled(control obj)376 int isenabled(control obj)
377 {
378     if (! obj)
379 	return 0;
380     return (obj->state & GA_Enabled) ? 1 : 0;
381 }
382 
383 /*
384  *  Checking and unchecking controls.
385  */
check(control obj)386 void check(control obj)
387 {
388     if (! obj)
389 	return;
390     switch (obj->kind) {
391     case CursorObject: case FontObject: case BitmapObject:
392     case MenubarObject: case MenuObject:
393 	break;
394     case MenuitemObject:
395 	CheckMenuItem(obj->parent->handle, obj->id,
396 		      MF_CHECKED | MF_BYCOMMAND);
397 	break;
398 #if USE_NATIVE_BUTTONS
399     case ButtonObject:
400 	sendmessage(obj->handle, BM_SETCHECK, 1, 0L);
401 	break;
402 #endif
403 #if USE_NATIVE_TOGGLES
404     case CheckboxObject: case RadioObject:
405 	sendmessage(obj->handle, BM_SETCHECK, 1, 0L);
406 	break;
407 #endif
408     default:
409 	if (! ischecked(obj)) {
410 	    obj->state |= GA_Checked;
411 	    draw(obj);
412 	}
413     }
414     obj->state |= GA_Checked;
415 
416 }
417 
uncheck(control obj)418 void uncheck(control obj)
419 {
420     if (! obj)
421 	return;
422     switch (obj->kind) {
423     case CursorObject: case FontObject: case BitmapObject:
424     case MenubarObject: case MenuObject:
425 	break;
426     case MenuitemObject:
427 	CheckMenuItem(obj->parent->handle, obj->id,
428 		      MF_UNCHECKED | MF_BYCOMMAND);
429 	break;
430 #if USE_NATIVE_BUTTONS
431     case ButtonObject:
432 	sendmessage(obj->handle, BM_SETCHECK, 0, 0L);
433 	break;
434 #endif
435 #if USE_NATIVE_TOGGLES
436     case CheckboxObject: case RadioObject:
437 	sendmessage(obj->handle, BM_SETCHECK, 0, 0L);
438 	break;
439 #endif
440     default:
441 	if (ischecked(obj)) {
442 	    obj->state &= ~GA_Checked;
443 	    draw(obj);
444 	}
445     }
446     obj->state &= ~GA_Checked;
447 }
448 
ischecked(control obj)449 int ischecked(control obj)
450 {
451     if (! obj)
452 	return 0;
453     return (obj->state & GA_Checked) ? 1 : 0;
454 }
455 
456 /*
457  *  Highlighting and unhighlighting controls.
458  */
highlight(control obj)459 void highlight(control obj)
460 {
461     if (! obj)
462 	return;
463     switch (obj->kind) {
464     case CursorObject: case FontObject: case BitmapObject:
465     case MenubarObject: case MenuObject: case MenuitemObject:
466 	break;
467 #if USE_NATIVE_BUTTONS
468     case ButtonObject:
469 	sendmessage(obj->handle, BM_SETSTATE, 1, 0L);
470 	break;
471 #endif
472 #if USE_NATIVE_TOGGLES
473     case CheckboxObject: case RadioObject:
474 	sendmessage(obj->handle, BM_SETSTATE, 1, 0L);
475 	break;
476 #endif
477     case FieldObject: case TextboxObject:
478 	sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,-1));
479 	break;
480     default:
481 	if (! ishighlighted(obj)) {
482 	    obj->state |= GA_Highlighted;
483 	    draw(obj);
484 	}
485     }
486     obj->state |= GA_Highlighted;
487 }
488 
unhighlight(control obj)489 void unhighlight(control obj)
490 {
491     if (! obj)
492 	return;
493     switch (obj->kind) {
494     case CursorObject: case FontObject: case BitmapObject:
495     case MenubarObject: case MenuObject: case MenuitemObject:
496 	break;
497 #if USE_NATIVE_BUTTONS
498     case ButtonObject:
499 	sendmessage(obj->handle, BM_SETSTATE, 0, 0L);
500 	break;
501 #endif
502 #if USE_NATIVE_TOGGLES
503     case CheckboxObject: case RadioObject:
504 	sendmessage(obj->handle, BM_SETSTATE, 0, 0L);
505 	break;
506 #endif
507     case FieldObject: case TextboxObject:
508 	sendmessage(obj->handle, EM_SETSEL, 0, MAKELONG(0,0));
509 	break;
510     default:
511 	if (ishighlighted(obj)) {
512 	    obj->state &= ~GA_Highlighted;
513 	    draw(obj);
514 	}
515     }
516     obj->state &= ~GA_Highlighted;
517 }
518 
ishighlighted(control obj)519 int ishighlighted(control obj)
520 {
521     if (! obj)
522 	return 0;
523     return (obj->state & GA_Highlighted) ? 1 : 0;
524 }
525 
526 /*
527  *  The flashcontrol function highlights a control as if the user
528  *  just used it, e.g. a button will become pressed down for a
529  *  moment. Used in conjunction with activatecontrol() below,
530  *  it can simulate a mouse-click on a button.
531  */
flashcontrol(control obj)532 void flashcontrol(control obj)
533 {
534     highlight(obj);
535     delay(150);
536     unhighlight(obj);
537 }
538 
539 /*
540  *  The activatecontrol function is really useful. It causes
541  *  a variety of controls to call their 'action functions.'
542  *
543  *  There are two types of action functions which a control can use.
544  *  The first (obj->action) takes one argument, the control itself,
545  *  and is used in buttons, checkboxes and the like as a response
546  *  to a user clicking on these controls. The second function,
547  *  (obj->hit) function, takes two arguments: the control and its
548  *  current 'value'. A scrollbar's value changes as the user scrolls,
549  *  and so its hit function is called whenever that happens.
550  *
551  *  The activatecontrol function tries to call the action function,
552  *  and if that doesn't exist, calls the hit function, passing the
553  *  object's value to it.
554  */
activatecontrol(control obj)555 void activatecontrol(control obj)
556 {
557     if (! obj)
558 	return;
559     drawto(obj);
560     if (obj->action != NULL)
561 	obj->action(obj);
562     else if (obj->hit != NULL)
563 	obj->hit(obj, obj->value);
564 }
565 
566 /*
567  *  Changing and determining the state of an object.
568  *  These functions do not automatically redraw the object.
569  */
570 #if 0
571 void setstate(control obj, long state)
572 {
573     if (obj)
574 	obj->state = state;
575 }
576 
577 long getstate(control obj)
578 {
579     if (obj)
580 	return obj->state;
581     else
582 	return 0;
583 }
584 #endif
585 
setvalue(control obj,int value)586 void setvalue(control obj, int value)
587 {
588     if (obj)
589 	obj->value = value;
590 }
591 
getvalue(control obj)592 int getvalue(control obj)
593 {
594     if (obj)
595 	return obj->value;
596     else
597 	return 0;
598 }
599 
setforeground(control obj,rgb fg)600 void setforeground(control obj, rgb fg)
601 {
602     if (! obj)
603 	return;
604     obj->fg = fg;
605     if (obj->kind == TextboxObject) {
606     	if (obj->handle) {
607     	    CHARFORMAT format;
608     	    COLORREF wincolour = RGB((fg&gaRed)>>16,(fg&gaGreen)>>8,(fg&gaBlue));
609     	    format.cbSize = sizeof(format);
610     	    format.dwMask = CFM_COLOR;
611     	    format.dwEffects = 0;
612     	    format.crTextColor = wincolour;
613 
614 	    sendmessage(obj->handle, EM_SETCHARFORMAT, 0, (LPARAM)&format);
615 	}
616     } else {
617     	InvalidateRect(obj->handle, NULL, TRUE);
618     	redraw(obj);
619     }
620 }
621 
getforeground(control obj)622 rgb getforeground(control obj)
623 {
624     if (obj)
625 	return obj->fg;
626     else
627 	return Black;
628 }
629 
setbackground(control obj,rgb bg)630 void setbackground(control obj, rgb bg)
631 {
632     COLORREF wincolour = RGB((bg&gaRed)>>16,(bg&gaGreen)>>8,(bg&gaBlue));
633 
634     if (! obj)
635 	return;
636     obj->bg = bg;
637     if (obj->kind == TextboxObject) {
638     	if (obj->handle)
639 	    sendmessage(obj->handle, EM_SETBKGNDCOLOR, 0, wincolour);
640     } else {
641     	if (obj->bgbrush)
642 	    DeleteObject(obj->bgbrush);
643     	obj->bgbrush = CreateSolidBrush(wincolour);
644 
645     	InvalidateRect(obj->handle, NULL, TRUE);
646     	redraw(obj);
647     }
648 }
649 
getbackground(control obj)650 rgb getbackground(control obj)
651 {
652     if (obj)
653 	return obj->bg;
654     else
655 	return Transparent;
656 }
657 
setdata(control obj,void * data)658 void setdata(control obj, void *data)
659 {
660     if (obj)
661 	obj->data = data;
662 }
663 
getdata(control obj)664 void *getdata(control obj)
665 {
666     if (obj)
667 	return obj->data;
668     else
669 	return NULL;
670 }
671 
672 /* These two are in none of the headers */
673 #ifdef UNUSED
_setextradata(control obj,void * data)674 void _setextradata(control obj, void *data)
675 {
676     if (obj)
677 	obj->extra = data;
678 }
679 
_getextradata(control obj)680 void *_getextradata(control obj)
681 {
682     if (obj)
683 	return obj->extra;
684     else
685 	return NULL;
686 }
687 #endif
688 
689 /*
690  *  Set the text of an object. This will set the names appearing
691  *  in a window's title bar, a button, checkbox or radio button,
692  *  or the value in a textbox or a text field.
693  */
settext(control obj,const char * text)694 void settext(control obj, const char *text)
695 {
696     char *old_text;
697 
698     if (! obj)
699 	return;
700     if (! text)
701 	text = "";
702     old_text = GA_gettext(obj);
703     if (old_text && strcmp(old_text, text) == 0)
704 	return; /* no changes to be made */
705     if (obj->text) {
706 	/* discard prior information */
707 	discard(obj->text);
708 	obj->text = NULL;
709     }
710     /* Set the new text. */
711     obj->text = new_string(text);
712     if (text) {
713 	if (obj->kind & ControlObject) {
714 	    text = to_dos_string(text);
715 	    if(localeCP > 0 && (localeCP != GetACP())) {
716 		/* This seems not actually to work */
717 		wchar_t *wc;
718 		int nc = strlen(text) + 1;
719 		wc = (wchar_t*) alloca(nc*sizeof(wchar_t));
720 		mbstowcs(wc, text, nc);
721 		SetWindowTextW(obj->handle, wc);
722 	    } else SetWindowText(obj->handle, text);
723 	    discard(text);
724 	}
725 	if (obj->kind == MenuitemObject) {
726 	    if(localeCP > 0 && (localeCP != GetACP())) {
727 		/* But this does */
728 		wchar_t wc[1000];
729 		mbstowcs(wc, text, 1000);
730 		ModifyMenuW(obj->parent->handle, obj->id,
731 			    MF_BYCOMMAND|MF_STRING, obj->id, wc);
732 
733 	    } else
734 		ModifyMenu(obj->parent->handle, obj->id,
735 			   MF_BYCOMMAND|MF_STRING, obj->id, text);
736 	}
737 
738     }
739     /* Redraw it if it's a redrawable object. */
740     if (obj->call && obj->call->redraw)
741 	redraw(obj);
742 }
743 
744 /*
745  *  Get the text string from a window's title bar or from a
746  *  control. This may be a button's name, for example, or
747  *  the value inside a text field.
748  */
GA_gettext(control obj)749 char *GA_gettext(control obj)
750 {
751     static char *empty = "";
752     char *text;
753     int length, index;
754     HWND hwnd;
755     UINT len_msg, gettext_msg;
756     WPARAM arg1, arg2;
757 
758     if (! obj)
759 	return empty;
760     if ((obj->kind & ControlObject) == 0)
761 	return obj->text ? obj->text : empty;
762 
763     hwnd = obj->handle;
764 
765     switch (obj->kind) {
766     case ListboxObject:
767     case MultilistObject:
768 	len_msg = LB_GETTEXTLEN;
769 	gettext_msg = LB_GETTEXT;
770 	index = getlistitem(obj);
771 	if (index < 0) return empty;
772 	arg1 = arg2 = index;
773 	break;
774     case DroplistObject:
775 	len_msg = CB_GETLBTEXTLEN;
776 	gettext_msg = CB_GETLBTEXT;
777 	index = getlistitem(obj);
778 	if (index < 0) return empty;
779 	arg1 = arg2 = index;
780 	break;
781     case DropfieldObject:
782 	// use default mechanism
783     default:
784 	len_msg = WM_GETTEXTLENGTH;
785 	gettext_msg = WM_GETTEXT;
786 	arg1 = 0;
787 	arg2 = sendmessage(hwnd, len_msg, 0, 0L)+1;
788 	break;
789     }
790 
791     /* Free any previous information. */
792     if (obj->text)
793 	discard(obj->text);
794     /* Find the length of the string. */
795     length = sendmessage(obj->handle, len_msg, arg1, 0L);
796     if (length == 0)
797 	return (obj->text = new_string(NULL));
798     /* Copy the text from the object. */
799     text = array(length+2, char);
800     sendmessage(obj->handle, gettext_msg, arg2, (LPCSTR) text);
801     obj->text = to_c_string(text);
802     discard(text);
803     /* Return the resultant string. */
804     if (! obj->text)
805 	obj->text = new_string(NULL);
806     return obj->text;
807 }
808 
809 /*
810  *  Set the font used by a control.
811  */
settextfont(object obj,font f)812 void settextfont(object obj, font f)
813 {
814     if (! obj)
815 	return;
816     if (! f)
817 	f = SystemFont;
818     if (obj->drawstate) {
819 	decrease_refcount(obj->drawstate->fnt);
820 	obj->drawstate->fnt = f;
821 	increase_refcount(f);
822     }
823     else {
824 	sendmessage(obj->handle, WM_SETFONT, f->handle, 0L);
825     }
826 }
827 
828 /*
829  *  Get the font used by a control.
830  */
gettextfont(object obj)831 font gettextfont(object obj)
832 {
833     font f = NULL;
834     if (obj) {
835 	if (obj->drawstate)
836 	    f = obj->drawstate->fnt;
837     }
838     if (! f)
839 	f = SystemFont;
840     return f;
841 }
842 
843 /*
844  *  Control and window functions.
845  *  Parentwindow of a window is itself.
846  */
parentwindow(control obj)847 window parentwindow(control obj)
848 {
849     while (obj) {
850 	if (obj->kind == WindowObject)
851 	    break;
852 	obj = obj->parent;
853     }
854     return (window) obj;
855 }
856 
857 /*
858  *  Polymorphic functions:
859  */
objrect(object obj)860 rect objrect(object obj)
861 {
862     rect r;
863     image img;
864 
865     if (! obj)
866 	return rect(0,0,0,0);
867 
868     switch (obj->kind)
869     {
870     case Image8: case Image32:
871 	img = (image) obj;
872 	r = rect(0,0,img->width,img->height);
873 	break;
874     case BitmapObject:
875     case FontObject:
876     case CursorObject:
877     case PrinterObject:
878 	r = obj->rect;
879 	break;
880     case MetafileObject:
881 	r = obj->rect;
882 	break;
883     default:
884 	GetClientRect(obj->handle, rect2RECT(&r));
885 	break;
886     }
887     return r;
888 }
889 
objwidth(object obj)890 int objwidth(object obj)
891 {
892     rect r = objrect(obj);
893     return r.width;
894 }
895 
objheight(object obj)896 int objheight(object obj)
897 {
898     rect r = objrect(obj);
899     return r.height;
900 }
901 
objdepth(object obj)902 int objdepth(object obj)
903 {
904     HDC screendc;
905     int depth;
906 
907     if (! obj)
908 	return 0;
909 
910     switch (obj->kind)
911     {
912     case Image8: case Image32:
913 	depth = ((image)obj)->depth;
914 	break;
915     case BitmapObject:
916     case FontObject: case CursorObject:
917 	depth = obj->depth;
918 	break;
919     default:
920 	screendc = GetDC(NULL);
921 	depth = GetDeviceCaps(screendc, BITSPIXEL) *
922 	    GetDeviceCaps(screendc, PLANES);
923 	ReleaseDC(NULL, screendc);
924 	break;
925     }
926 
927     return depth;
928 }
929 
delobj(object obj)930 void delobj(object obj)
931 {
932     if (! obj)
933 	return;
934     switch (obj->kind)
935     {
936     case Image8:
937     case Image32:
938 	delimage((image)obj);
939 	break;
940     default:
941 	/* if (obj->refcount == 1)   why would this test be here?? */
942 	decrease_refcount(obj);
943 	break;
944     }
945 }
946 
setcaret(object obj,int x,int y,int width,int height)947 void setcaret(object obj, int x, int y, int width, int height)
948 {
949     if (! obj)
950     	return;
951     if (width != obj->caretwidth || height != obj->caretheight) {
952 	if (obj->caretwidth > 0 && (obj->state & GA_Focus)) DestroyCaret();
953 	obj->caretwidth = width;
954 	obj->caretheight = height;
955 	if (width > 0) {
956 	    if (obj->state & GA_Focus)
957 		CreateCaret(obj->handle, (HBITMAP) NULL, width, height);
958 	    obj->caretshowing = 0;
959 	}
960     }
961     if (obj->state & GA_Focus)
962     	SetCaretPos(x, y);
963 }
964 
showcaret(object obj,int showing)965 void showcaret(object obj, int showing)
966 {
967     if (! obj || showing == obj->caretshowing)
968     	return;
969     obj->caretshowing = showing;
970     if (showing)
971     	ShowCaret(obj->handle);
972     else
973     	HideCaret(obj->handle);
974 }
975