1 /* Copyright (C) 2000-2012 by George Williams */
2 /*
3  * Redistribution and use in source and binary forms, with or without
4  * modification, are permitted provided that the following conditions are met:
5 
6  * Redistributions of source code must retain the above copyright notice, this
7  * list of conditions and the following disclaimer.
8 
9  * Redistributions in binary form must reproduce the above copyright notice,
10  * this list of conditions and the following disclaimer in the documentation
11  * and/or other materials provided with the distribution.
12 
13  * The name of the author may not be used to endorse or promote products
14  * derived from this software without specific prior written permission.
15 
16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17  * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19  * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22  * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24  * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25  * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 #include <fontforge-config.h>
29 
30 #include "gdraw.h"
31 #include "ggadgetP.h"
32 #include "gkeysym.h"
33 #include "gresource.h"
34 #include "ustring.h"
35 
36 #include <math.h>
37 
38 static GBox radio_box = GBOX_EMPTY; /* Don't initialize here */
39 static GBox radio_on_box = GBOX_EMPTY; /* Don't initialize here */
40 static GBox radio_off_box = GBOX_EMPTY; /* Don't initialize here */
41 static GBox checkbox_box = GBOX_EMPTY; /* Don't initialize here */
42 static GBox checkbox_on_box = GBOX_EMPTY; /* Don't initialize here */
43 static GBox checkbox_off_box = GBOX_EMPTY; /* Don't initialize here */
44 static GBox visibility_on_box = GBOX_EMPTY; /* Don't initialize here */
45 static GBox visibility_off_box = GBOX_EMPTY; /* Don't initialize here */
46 static GResImage *radon, *radoff, *checkon, *checkoff, *raddison, *raddisoff, *checkdison, *checkdisoff;
47 static GResImage *visibilityon, *visibilityoff, *visibilitydison, *visibilitydisoff;
48 static FontInstance *checkbox_font = NULL;
49 static int gradio_inited = false;
50 
51 static GResInfo gradio_ri, gradioon_ri, gradiooff_ri;
52 static GResInfo gcheckbox_ri, gcheckboxon_ri, gcheckboxoff_ri;
53 static GTextInfo radio_lab[] = {
54     { (unichar_t *) "Disabled On", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
55     { (unichar_t *) "Disabled Off", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
56     { (unichar_t *) "Enabled" , NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
57     { (unichar_t *) "Enabled 2" , NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' }
58 };
59 static GGadgetCreateData radio_gcd[] = {
60     { GRadioCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &radio_lab[0], { NULL }, gg_visible, NULL, NULL }, NULL, NULL },
61     { GRadioCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &radio_lab[1], { NULL }, gg_visible|gg_cb_on, NULL, NULL }, NULL, NULL },
62     { GRadioCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &radio_lab[2], { NULL }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL },
63     { GRadioCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &radio_lab[3], { NULL }, gg_visible|gg_enabled|gg_cb_on, NULL, NULL }, NULL, NULL }
64 };
65 static GGadgetCreateData *rarray[] = { GCD_Glue, &radio_gcd[0], GCD_Glue, &radio_gcd[1], GCD_Glue, &radio_gcd[2], GCD_Glue, &radio_gcd[3], GCD_Glue, NULL, NULL };
66 static GGadgetCreateData radiobox =
67     { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) rarray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
68 static GResInfo gradio_ri = {
69     &gradioon_ri, &ggadget_ri,&gradioon_ri, &gradiooff_ri,
70     &radio_box,
71     &checkbox_font,
72     &radiobox,
73     NULL,
74     N_("Radio Button"),
75     N_("Radio Button"),
76     "GRadio",
77     "Gdraw",
78     false,
79     omf_border_type|omf_padding,
80     NULL,
81     GBOX_EMPTY,
82     NULL,
83     NULL,
84     NULL
85 };
86 static struct resed gradioon_re[] = {
87     { N_("Image"), "Image", rt_image, &radon, N_("Image used instead of the Radio On Mark"), NULL, { 0 }, 0, 0 },
88     { N_("Disabled Image"), "DisabledImage", rt_image, &raddison, N_("Image used instead of the Radio On Mark (when the radio is disabled)"), NULL, { 0 }, 0, 0 },
89     RESED_EMPTY
90 };
91 static GResInfo gradioon_ri = {
92     &gradiooff_ri, &ggadget_ri,&gradiooff_ri, &gradio_ri,
93     &radio_on_box,
94     NULL,
95     &radiobox,
96     gradioon_re,
97     N_("Radio On Mark"),
98     N_("The mark showing a radio button is on (depressed, selected)"),
99     "GRadioOn",
100     "Gdraw",
101     false,
102     omf_border_type|omf_border_shape|box_do_depressed_background,
103     NULL,
104     GBOX_EMPTY,
105     NULL,
106     NULL,
107     NULL
108 };
109 static struct resed gradiooff_re[] = {
110     { N_("Image"), "Image", rt_image, &radoff, N_("Image used instead of the Radio Off Mark"), NULL, { 0 }, 0, 0 },
111     { N_("Disabled Image"), "DisabledImage", rt_image, &raddisoff, N_("Image used instead of the Radio Off Mark (when the radio is disabled)"), NULL, { 0 }, 0, 0 },
112     RESED_EMPTY
113 };
114 static GResInfo gradiooff_ri = {
115     &gcheckbox_ri, &ggadget_ri,&gradioon_ri, &gradio_ri,
116     &radio_off_box,
117     NULL,
118     &radiobox,
119     gradiooff_re,
120     N_("Radio Off Mark"),
121     N_("The mark showing a radio button is off (up, not selected)"),
122     "GRadioOff",
123     "Gdraw",
124     false,
125     omf_border_type|omf_border_shape|box_do_depressed_background,
126     NULL,
127     GBOX_EMPTY,
128     NULL,
129     NULL,
130     NULL
131 };
132 static GTextInfo checkbox_lab[] = {
133     { (unichar_t *) "Disabled On", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
134     { (unichar_t *) "Disabled Off", NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' },
135     { (unichar_t *) "Enabled" , NULL, 0, 0, NULL, NULL, 0, 0, 0, 0, 0, 0, 1, 0, 0, '\0' }
136 };
137 static GGadgetCreateData checkbox_gcd[] = {
138     { GCheckBoxCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &checkbox_lab[0], { NULL }, gg_visible, NULL, NULL }, NULL, NULL },
139     { GCheckBoxCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &checkbox_lab[1], { NULL }, gg_visible|gg_cb_on, NULL, NULL }, NULL, NULL },
140     { GCheckBoxCreate, { GRECT_EMPTY, NULL, 0, 0, 0, 0, 0, &checkbox_lab[1], { NULL }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL }
141 };
142 static GGadgetCreateData *carray[] = { GCD_Glue, &checkbox_gcd[0], GCD_Glue, &checkbox_gcd[1], GCD_Glue, &checkbox_gcd[2], GCD_Glue, NULL, NULL };
143 static GGadgetCreateData checkboxbox =
144     { GHVGroupCreate, { { 2, 2, 0, 0 }, NULL, 0, 0, 0, 0, 0, NULL, { (GTextInfo *) carray }, gg_visible|gg_enabled, NULL, NULL }, NULL, NULL };
145 static GResInfo gcheckbox_ri = {
146     &gcheckboxon_ri, &ggadget_ri,&gcheckboxon_ri, &gcheckboxoff_ri,
147     &checkbox_box,
148     &checkbox_font,
149     &checkboxbox,
150     NULL,
151     N_("Check Box"),
152     N_("Check Box"),
153     "GCheckBox",
154     "Gdraw",
155     false,
156     omf_border_type|omf_padding,
157     NULL,
158     GBOX_EMPTY,
159     NULL,
160     NULL,
161     NULL
162 };
163 static struct resed gcheckboxon_re[] = {
164     {N_("Image"), "Image", rt_image, &checkon, N_("Image used instead of the Radio On Mark"), NULL, { 0 }, 0, 0 },
165     {N_("Disabled Image"), "DisabledImage", rt_image, &checkdison, N_("Image used instead of the Radio On Mark (when the radio is disabled)"), NULL, { 0 }, 0, 0 },
166     RESED_EMPTY
167 };
168 static GResInfo gcheckboxon_ri = {
169     &gcheckboxoff_ri, &ggadget_ri,&gcheckboxoff_ri, &gcheckbox_ri,
170     &checkbox_on_box,
171     NULL,
172     &checkboxbox,
173     gcheckboxon_re,
174     N_("Check Box On Mark"),
175     N_("The mark showing a checkbox is on (depressed, selected)"),
176     "GCheckBoxOn",
177     "Gdraw",
178     false,
179     omf_border_type|omf_border_shape|box_do_depressed_background,
180     NULL,
181     GBOX_EMPTY,
182     NULL,
183     NULL,
184     NULL
185 
186 };
187 static struct resed gcheckboxoff_re[] = {
188     {N_("Image"), "Image", rt_image, &checkoff, N_("Image used instead of the Check Box Off Mark"), NULL, { 0 }, 0, 0 },
189     {N_("Disabled Image"), "DisabledImage", rt_image, &checkdisoff, N_("Image used instead of the Check Box Off Mark (when the radio is disabled)"), NULL, { 0 }, 0, 0 },
190     RESED_EMPTY
191 };
192 static GResInfo gcheckboxoff_ri = {
193     NULL, &ggadget_ri,&gcheckboxon_ri, &gcheckbox_ri,
194     &checkbox_off_box,
195     NULL,
196     &checkboxbox,
197     gcheckboxoff_re,
198     N_("Check Box Off Mark"),
199     N_("The mark showing a checkbox is off (up, not selected)"),
200     "GCheckBoxOff",
201     "Gdraw",
202     false,
203     omf_border_type|omf_border_shape|box_do_depressed_background,
204     NULL,
205     GBOX_EMPTY,
206     NULL,
207     NULL,
208     NULL
209 };
210 
GRadioChanged(GRadio * gr)211 static void GRadioChanged(GRadio *gr) {
212     GEvent e;
213 
214     if ( gr->isradio && gr->ison )
215 return;		/* Do Nothing, it's already on */
216     else if ( gr->isradio ) {
217 	GRadio *other;
218 	for ( other=gr->post; other!=gr; other = other->post ) {
219 	    if ( other->ison ) {
220 		other->ison = false;
221 		_ggadget_redraw((GGadget *) other);
222 	    }
223 	}
224     } else {
225 	/* Checkboxes just default down */
226 	;
227     }
228     gr->ison = !gr->ison;
229     e.type = et_controlevent;
230     e.w = gr->g.base;
231     e.u.control.subtype = et_radiochanged;
232     e.u.control.g = &gr->g;
233     if ( gr->g.handle_controlevent != NULL )
234 	(gr->g.handle_controlevent)(&gr->g,&e);
235     else
236 	GDrawPostEvent(&e);
237 }
238 
239 /* Return the number of lines in the label of a radio button. */
gradio_linecount(GRadio * gr)240 static int gradio_linecount(GRadio *gr) {
241     int lcnt;
242     unichar_t *pt;
243 
244     lcnt = 0;
245     if ( gr->label!=NULL ) {
246 	for ( pt = gr->label; ; ) {
247 	    for ( ; *pt!='\0' && *pt!='\n'; ++pt );
248 	    ++lcnt;
249 	    if ( *pt=='\0' )
250 	break;
251 	    ++pt;
252 	}
253     }
254 return( lcnt );
255 }
256 
257 /* Called on expose events, this renders the button. */
gradio_expose(GWindow pixmap,GGadget * g,GEvent * event)258 static int gradio_expose(GWindow pixmap, GGadget *g, GEvent *event) {
259     GRadio *gr = (GRadio *) g;
260     int x;
261     GImage *img = gr->image; /* the optional image tied to the label */
262     GResImage *mark;
263     GRect old1, old2, old3;
264     int yoff = (g->inner.height-(gr->fh))/2;
265 
266     if ( g->state == gs_invisible )
267 return( false );
268 
269      /* First blank out the button area. */
270     GDrawPushClip(pixmap,&g->r,&old1);
271 
272     GBoxDrawBackground(pixmap,&g->r,g->box,
273 	    g->state==gs_enabled? gs_pressedactive: g->state,false);
274     GBoxDrawBorder(pixmap,&g->r,g->box,g->state,false);
275 
276     GDrawPushClip(pixmap,&gr->onoffrect,&old2);
277     GBoxDrawBackground(pixmap,&gr->onoffrect,gr->ison?gr->onbox:gr->offbox,
278 	    gs_pressedactive,false);
279     if (gr->ison && gr->onbox->border_type!=bt_none)
280         GBoxDrawBorder(pixmap,&gr->onoffrect,gr->onbox, gs_pressedactive,false);
281     else if (!gr->ison && gr->offbox->border_type!=bt_none)
282         GBoxDrawBorder(pixmap,&gr->onoffrect,gr->offbox,gs_pressedactive,false);
283 
284      /* Next draw either the right image or draw in an on or off indicator. */
285     mark = NULL;
286     if ( g->state == gs_disabled )
287 	mark = gr->ison ? gr->ondis : gr->offdis; /* note: ondis or offdis may be NULL! */
288     if ( mark==NULL || mark->image==NULL )
289 	mark = gr->ison ? gr->on : gr->off; /* note: on or off may be NULL! */
290     if ( mark!=NULL && mark->image==NULL ) /* when there's a reference to a special image, but no actual image */
291 	mark = NULL;
292     if ( mark!=NULL ) {
293 	GDrawPushClip(pixmap,&gr->onoffinner,&old3);
294 	GDrawDrawScaledImage(pixmap,mark->image,
295 		gr->onoffinner.x,gr->onoffinner.y);
296 	GDrawPopClip(pixmap,&old3);
297     } else if ( gr->ison && gr->onbox == &checkbox_on_box ) {
298 	 /* for radio buttons where the on is a checkbox style, draw an X */
299 	Color fg = g->state==gs_disabled?g->box->disabled_foreground:
300 			g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
301 			g->box->main_foreground;
302 	int bp = GDrawPointsToPixels(pixmap,gr->onbox->border_width);
303 	GDrawDrawLine(pixmap, gr->onoffrect.x+bp,gr->onoffrect.y+bp,
304 				gr->onoffrect.x+gr->onoffrect.width-1-bp,gr->onoffrect.y+gr->onoffrect.height-1-bp,
305 			        fg);
306 	GDrawDrawLine(pixmap, gr->onoffrect.x+gr->onoffrect.width-1-bp,gr->onoffrect.y+bp,
307 				gr->onoffrect.x+bp,gr->onoffrect.y+gr->onoffrect.height-1-bp,
308 			        fg);
309     } else if ( gr->ison && gr->onbox == &visibility_on_box ) {
310          /* draw open white of eye */
311         GPoint pts[15];
312         Color fg;
313         double angle;
314         int c,i;
315 	int bp = gr->onbox->border_type==bt_none ? 0 : GDrawPointsToPixels(pixmap,gr->onbox->border_width);
316         int x=gr->onoffrect.x+bp;
317         int y=gr->onoffrect.y+bp;
318         int w=gr->onoffrect.width -1-2*bp;
319         int h=gr->onoffrect.height-1-2*bp;
320         GRect rect;
321         for (c=0, i=0; c<7; c++) {
322             angle=(30+c/6.*120)*FF_PI/180;
323             pts[i].x=.5*w*cos(angle)+x+w/2;
324             pts[i].y=.5*h*sin(angle)+y+h/4;
325             ++i;
326         }
327         for (c=1; c<6; c++) {
328             angle=(180+30+c/6.*120)*FF_PI/180;
329             pts[i].x=.5*w*cos(angle)+x+w/2;
330             pts[i].y=.5*h*sin(angle)+y+h*3/4;
331             ++i;
332         }
333         pts[i].x=pts[0].x;
334         pts[i].y=pts[0].y;
335         ++i;
336         fg=0x00ffffff; /* white */
337         GDrawFillPoly(pixmap, pts, i, fg);
338 	fg = g->state==gs_disabled?g->box->disabled_foreground:
339 			g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
340 			g->box->main_foreground;
341         GDrawDrawPoly(pixmap, pts, i, fg);
342 
343          /* draw pupil */
344         rect.x=gr->onoffrect.x+bp+w*.3;
345         rect.y=gr->onoffrect.y+bp+h*.3;
346         rect.width =.4*w;
347         rect.height=.4*h;
348         fg=0; /* black */
349         GDrawFillElipse(pixmap, &rect, fg);
350 
351     } else if ( (!gr->ison) && gr->onbox == &visibility_on_box ) {
352          /* draw closed eye */
353         GPoint pts[7];
354         double angle;
355 	int bp = gr->onbox->border_type==bt_none ? 0 : GDrawPointsToPixels(pixmap,gr->onbox->border_width);
356         int x=gr->onoffrect.x+bp;
357         int y=gr->onoffrect.y+bp;
358         int w=gr->onoffrect.width -1-2*bp;
359         int h=gr->onoffrect.height-1-2*bp;
360         Color fg = g->state==gs_disabled?g->box->disabled_foreground:
361 			g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
362 			g->box->main_foreground;
363         for (int i = 0; i <= 6; i++) {
364             angle=(30+i/6.*120)*FF_PI/180;
365             pts[i].x=.5*w*cos(angle)+x+w/2;
366             pts[i].y=.5*h*sin(angle)+y+h/4;
367 
368              /* draw lashes */
369             if (i>0 && i<6) GDrawDrawLine(pixmap, pts[i].x,pts[i].y, .75*w*cos(angle)+x+w/2, .75*h*sin(angle)+y+h/4, fg);
370         }
371         GDrawDrawPoly(pixmap, pts, sizeof(pts)/sizeof(pts[0]), fg);
372     }
373 
374     GDrawPopClip(pixmap,&old2);
375     x = gr->onoffrect.x + gr->onoffrect.width + GDrawPointsToPixels(pixmap,4);
376 
377      /* Finally write out the label if any. */
378     GDrawPushClip(pixmap,&g->inner,&old2);
379     if ( gr->font!=NULL )
380 	GDrawSetFont(pixmap,gr->font);
381     if ( gr->image_precedes && img!=NULL ) {
382 	GDrawDrawScaledImage(pixmap,img,x,g->inner.y);
383 	x += GImageGetScaledWidth(pixmap,img) + GDrawPointsToPixels(pixmap,_GGadget_TextImageSkip);
384     }
385     if ( gr->label!=NULL ) {
386 	Color fg = g->state==gs_disabled?g->box->disabled_foreground:
387 			g->box->main_foreground==COLOR_DEFAULT?GDrawGetDefaultForeground(GDrawGetDisplayOfWindow(pixmap)):
388 			g->box->main_foreground;
389 	int lcnt = gradio_linecount(gr);
390 	if ( lcnt>1 )
391 	    yoff = (g->inner.height-lcnt*gr->fh)/2;
392 	_ggadget_underlineMnemonic(pixmap,x,g->inner.y + gr->as + yoff,gr->label,
393 		g->mnemonic,fg,g->inner.y+g->inner.height);
394 	x += GDrawDrawText(pixmap,x,g->inner.y + gr->as + yoff,gr->label,-1,fg);
395 	x += GDrawPointsToPixels(pixmap,_GGadget_TextImageSkip);
396     }
397     if ( !gr->image_precedes && img!=NULL )
398 	GDrawDrawScaledImage(pixmap,img,x,g->inner.y);
399 
400     GDrawPopClip(pixmap,&old2);
401     GDrawPopClip(pixmap,&old1);
402 return( true );
403 }
404 
gradio_mouse(GGadget * g,GEvent * event)405 static int gradio_mouse(GGadget *g, GEvent *event) {
406     GRadio *gr = (GRadio *) g;
407     int within = gr->within, pressed = gr->pressed;
408 
409     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
410 return( false );
411 
412     if ( event->type == et_crossing ) {
413 	if ( gr->within && !event->u.crossing.entered )
414 	    gr->within = false;
415     } else if ( gr->pressed && event->type!=et_mousemove ) {
416 	if ( event->type == et_mousedown ) /* They pressed 2 mouse buttons? */
417 	    gr->pressed = false;
418 	else if ( GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
419 	    gr->pressed = false;
420 	    if ( !gr->isradio || !gr->ison )
421 		GRadioChanged(gr);
422 	} else if ( event->type == et_mouseup )
423 	    gr->pressed = false;
424 	else
425 	    gr->within = true;
426     } else if ( event->type == et_mousedown &&
427 	    GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
428 	gr->pressed = true;
429 	gr->within = true;
430     } else if ( event->type == et_mousemove &&
431 	    GGadgetWithin(g,event->u.mouse.x,event->u.mouse.y)) {
432 	gr->within = true;
433 	if ( !gr->pressed && g->popup_msg )
434 	    GGadgetPreparePopup(g->base,g->popup_msg);
435     } else if ( event->type == et_mousemove && gr->within ) {
436 	gr->within = false;
437     } else {
438 return( false );
439     }
440     if ( within != gr->within )
441 	g->state = gr->within? gs_active : gs_enabled;
442     if ( within != gr->within || pressed != gr->pressed )
443 	_ggadget_redraw(g);
444 return( gr->within );
445 }
446 
gradio_key(GGadget * g,GEvent * event)447 static int gradio_key(GGadget *g, GEvent *event) {
448     GRadio *gr = (GRadio *) g;
449 
450     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active && g->state!=gs_focused ))
451 return(false);
452 
453     if ( event->u.chr.keysym == GK_Return || event->u.chr.keysym == GK_Tab ||
454 	    event->u.chr.keysym == GK_BackTab || event->u.chr.keysym == GK_Escape )
455 return( false );
456 
457     if (event->type == et_char && event->u.chr.chars[0]==' ' ) {
458 	GRadioChanged(gr);
459 	_ggadget_redraw(g);
460 return( true );
461     }
462 return( false );
463 }
464 
gradio_focus(GGadget * g,GEvent * event)465 static int gradio_focus(GGadget *g, GEvent *event) {
466     GRadio *gr = (GRadio *) g;
467 
468     if ( !g->takes_input || (g->state!=gs_enabled && g->state!=gs_active ))
469 return(false);
470 
471     if ( event->u.focus.mnemonic_focus==mf_shortcut ) {
472 	GRadioChanged(gr);
473     }
474 return( true );
475 }
476 
gradio_destroy(GGadget * g)477 static void gradio_destroy(GGadget *g) {
478     GRadio *gr = (GRadio *) g;
479 
480     if ( gr==NULL )
481 return;
482     if ( gr->isradio && gr->post!=gr ) {
483 	GRadio *prev;
484 	for ( prev=gr->post; prev->post!=gr; prev = prev->post );
485 	prev->post = gr->post;
486     }
487     free(gr->label);
488     _ggadget_destroy(g);
489 }
490 
GRadioSetTitle(GGadget * g,const unichar_t * tit)491 static void GRadioSetTitle(GGadget *g,const unichar_t *tit) {
492     GRadio *b = (GRadio *) g;
493     free(b->label);
494     b->label = u_copy(tit);
495 }
496 
_GRadioGetTitle(GGadget * g)497 static const unichar_t *_GRadioGetTitle(GGadget *g) {
498     GRadio *b = (GRadio *) g;
499 return( b->label );
500 }
501 
GRadioSetImageTitle(GGadget * g,GImage * img,const unichar_t * tit,int before)502 static void GRadioSetImageTitle(GGadget *g,GImage *img,const unichar_t *tit, int before) {
503     GRadio *b = (GRadio *) g;
504     if ( b->g.free_box )
505 	free( b->g.box );
506     free(b->label);
507     b->label = u_copy(tit);
508     b->image = img;
509     b->image_precedes = before;
510     _ggadget_redraw(g);
511 }
512 
GRadioGetImage(GGadget * g)513 static GImage *GRadioGetImage(GGadget *g) {
514     GRadio *b = (GRadio *) g;
515 return( b->image );
516 }
517 
GRadioSetFont(GGadget * g,FontInstance * new)518 static void GRadioSetFont(GGadget *g,FontInstance *new) {
519     GRadio *b = (GRadio *) g;
520     b->font = new;
521 }
522 
GRadioGetFont(GGadget * g)523 static FontInstance *GRadioGetFont(GGadget *g) {
524     GRadio *b = (GRadio *) g;
525 return( b->font );
526 }
527 
_gradio_move(GGadget * g,int32 x,int32 y)528 static void _gradio_move(GGadget *g, int32 x, int32 y ) {
529     GRadio *b = (GRadio *) g;
530     b->onoffrect.x = x+(b->onoffrect.x-g->r.x);
531     b->onoffinner.x = x+(b->onoffinner.x-g->r.x);
532     _ggadget_move(g,x,y);
533     b->onoffrect.y = g->r.y+(g->r.height-b->onoffrect.height)/2;
534     b->onoffinner.y = g->r.y+(g->r.height-b->onoffinner.height)/2;
535 }
536 
GRadioGetDesiredSize(GGadget * g,GRect * outer,GRect * inner)537 static void GRadioGetDesiredSize(GGadget *g, GRect *outer, GRect *inner) {
538     GCheckBox *gl = (GCheckBox *) g;
539     int iwidth=0, iheight=0;
540     GTextBounds bounds;
541     int as=0, ds, ld, fh=0, width=0;
542 
543     if ( gl->image!=NULL ) {
544 	iwidth = GImageGetScaledWidth(gl->g.base,gl->image);
545 	iheight = GImageGetScaledHeight(gl->g.base,gl->image);
546     }
547     GDrawWindowFontMetrics(g->base,gl->font,&as, &ds, &ld);
548     if ( gl->label!=NULL ) {
549 	FontInstance *old = GDrawSetFont(gl->g.base,gl->font);
550 	width = GDrawGetTextBounds(gl->g.base,gl->label, -1, &bounds);
551 	GDrawSetFont(gl->g.base,old);
552 	if ( as<bounds.as ) as = bounds.as;
553 	if ( ds<bounds.ds ) ds = bounds.ds;
554     }
555     fh = as+ds;
556 
557     if ( width!=0 && iwidth!=0 )
558 	width += GDrawPointsToPixels(gl->g.base,_GGadget_TextImageSkip);
559     width += iwidth;
560     if ( iheight<fh )
561 	iheight = fh;
562     if ( iheight < gl->onoffrect.height )
563 	iheight = gl->onoffrect.height;
564     width += gl->onoffrect.width + GDrawPointsToPixels(gl->g.base,5);
565     if ( g->desired_width>0 ) width = g->desired_width;
566     if ( g->desired_height>0 ) iheight = g->desired_height;
567     if ( inner!=NULL ) {
568 	inner->x = inner->y = 0;
569 	inner->width = width;
570 	inner->height = iheight;
571     }
572     if ( outer!=NULL ) {
573 	outer->x = outer->y = 0;
574 	outer->width = width;
575 	outer->height = iheight;
576 	/*_ggadgetFigureSize(gl->g.base,gl->g.box,outer,false);*/
577     }
578 }
579 
GVisibilityBoxSetToMinWH(GGadget * g)580 void GVisibilityBoxSetToMinWH(GGadget *g)
581 {
582     GRect size;
583     GRect outer;
584     GRect inner;
585     GRadioGetDesiredSize(g, &outer, &inner );
586     outer.width = MIN(outer.width,outer.height);
587     outer.width -= 3;
588     size.width  = outer.width;
589     size.height = outer.height;
590     GGadgetSetSize(g,&size);
591 }
592 
593 
594 struct gfuncs gradio_funcs = {
595     0,
596     sizeof(struct gfuncs),
597 
598     gradio_expose,
599     gradio_mouse,
600     gradio_key,
601     NULL,
602     gradio_focus,
603     NULL,
604     NULL,
605 
606     _ggadget_redraw,
607     _gradio_move,
608     _ggadget_resize,
609     _ggadget_setvisible,
610     _ggadget_setenabled,
611     _ggadget_getsize,
612     _ggadget_getinnersize,
613 
614     gradio_destroy,
615 
616     GRadioSetTitle,
617     _GRadioGetTitle,
618     NULL,
619     GRadioSetImageTitle,
620     GRadioGetImage,
621 
622     GRadioSetFont,
623     GRadioGetFont,
624 
625     NULL,
626     NULL,
627     NULL,
628     NULL,
629     NULL,
630     NULL,
631     NULL,
632     NULL,
633     NULL,
634     NULL,
635     NULL,
636 
637     GRadioGetDesiredSize,
638     _ggadget_setDesiredSize,
639     NULL,
640     NULL
641 };
642 
GRadioInit()643 static void GRadioInit() {
644     _GGadgetCopyDefaultBox(&radio_box);
645     _GGadgetCopyDefaultBox(&radio_on_box);
646     _GGadgetCopyDefaultBox(&radio_off_box);
647     _GGadgetCopyDefaultBox(&checkbox_box);
648     _GGadgetCopyDefaultBox(&checkbox_on_box);
649     _GGadgetCopyDefaultBox(&checkbox_off_box);
650     _GGadgetCopyDefaultBox(&visibility_on_box);
651     _GGadgetCopyDefaultBox(&visibility_off_box);
652 
653     radio_box.padding = 0;
654     radio_box.border_type = bt_none;
655     radio_on_box.border_type = bt_raised;
656     radio_off_box.border_type = bt_lowered;
657     radio_on_box.border_shape = radio_off_box.border_shape = bs_diamond;
658     radio_on_box.flags = radio_off_box.flags |= box_do_depressed_background;
659 
660     checkbox_box.padding = 0;
661     checkbox_box.border_type = bt_none;
662     checkbox_on_box.border_type = bt_raised;
663     checkbox_off_box.border_type = bt_lowered;
664     checkbox_on_box.flags = checkbox_off_box.flags |= box_do_depressed_background;
665     checkbox_font = _GGadgetInitDefaultBox("GRadio.",&radio_box,NULL);
666     checkbox_font = _GGadgetInitDefaultBox("GCheckBox.",&checkbox_box,checkbox_font);
667 
668     visibility_on_box.border_type=bt_none;
669     visibility_on_box.padding=1;
670     visibility_off_box.border_type=bt_none;
671     visibility_off_box.padding=1;
672 
673     _GGadgetInitDefaultBox("GRadioOn.",&radio_on_box,NULL);
674     _GGadgetInitDefaultBox("GRadioOff.",&radio_off_box,NULL);
675     _GGadgetInitDefaultBox("GCheckBoxOn.",&checkbox_on_box,NULL);
676     _GGadgetInitDefaultBox("GCheckBoxOff.",&checkbox_off_box,NULL);
677     _GGadgetInitDefaultBox("GVisibilityBoxOn.",&visibility_on_box,NULL);
678     _GGadgetInitDefaultBox("GVisibitityBoxOff.",&visibility_off_box,NULL);
679 
680     if ( radio_on_box.depressed_background == radio_off_box.depressed_background ) {
681 	radio_on_box.depressed_background = radio_on_box.active_border;
682 	radio_off_box.depressed_background = radio_off_box.main_background;
683     }
684 
685     if ( checkbox_on_box.depressed_background == checkbox_off_box.depressed_background ) {
686 	checkbox_on_box.depressed_background = checkbox_on_box.active_border;
687 	checkbox_off_box.depressed_background = checkbox_off_box.main_background;
688     }
689 
690     radon = GGadgetResourceFindImage("GRadioOn.Image",NULL);
691     radoff = GGadgetResourceFindImage("GRadioOff.Image",NULL);
692     raddison = GGadgetResourceFindImage("GRadioOn.DisabledImage",NULL);
693     raddisoff = GGadgetResourceFindImage("GRadioOff.DisabledImage",NULL);
694 
695     checkon = GGadgetResourceFindImage("GCheckBoxOn.Image",NULL);
696     checkoff = GGadgetResourceFindImage("GCheckBoxOff.Image",NULL);
697     checkdison = GGadgetResourceFindImage("GCheckBoxOn.DisabledImage",NULL);
698     checkdisoff = GGadgetResourceFindImage("GCheckBoxOff.DisabledImage",NULL);
699 
700     visibilityon = GGadgetResourceFindImage("GVisibilityBoxOn.Image",NULL);
701     visibilityoff = GGadgetResourceFindImage("GVisibilityBoxOff.Image",NULL);
702     visibilitydison = GGadgetResourceFindImage("GVisibilityBoxOn.DisabledImage",NULL);
703     visibilitydisoff = GGadgetResourceFindImage("GVisibilityBoxOff.DisabledImage",NULL);
704 
705     gradio_inited = true;
706 }
707 
GCheckBoxFit(GCheckBox * gl)708 static void GCheckBoxFit(GCheckBox *gl) {
709     int as=0, ds, ld;
710     GRect outer, inner, needed;
711     int iwidth, iheight;
712 
713     needed.x = needed.y = 0;
714     needed.width = needed.height = 1;
715     if ( gl->on!=NULL && gl->on->image!=NULL ) {
716 	if (( iwidth = GImageGetScaledWidth(gl->g.base,gl->on->image))>needed.width )
717 	    needed.width = iwidth;
718 	if (( iheight = GImageGetScaledHeight(gl->g.base,gl->on->image))>needed.height )
719 	    needed.height = iheight;
720     }
721     if ( gl->off!=NULL && gl->off->image!=NULL ) {
722 	if (( iwidth = GImageGetScaledWidth(gl->g.base,gl->off->image))>needed.width )
723 	    needed.width = iwidth;
724 	if (( iheight = GImageGetScaledHeight(gl->g.base,gl->off->image))>needed.height )
725 	    needed.height = iheight;
726     }
727     gl->onoffinner = needed;
728     _ggadgetFigureSize(gl->g.base,gl->onbox,&needed,false);
729     gl->onoffrect = needed;
730 
731     GDrawWindowFontMetrics(gl->g.base,gl->font,&as, &ds, &ld);
732     GRadioGetDesiredSize(&gl->g,&outer,&inner);
733     _ggadgetSetRects(&gl->g,&outer, &inner, -1, 0);
734 
735     gl->as = as;
736     gl->fh = as+ds;
737 
738     gl->onoffrect.x = gl->g.inner.x;
739     gl->onoffrect.y = (gl->as>gl->onoffrect.height)?gl->g.inner.y+gl->as-gl->onoffrect.height:
740 	    gl->g.inner.y+(gl->g.inner.height-gl->onoffrect.height)/2;
741     gl->onoffinner.x = gl->onoffrect.x + (gl->onoffrect.width-gl->onoffinner.width)/2;
742     gl->onoffinner.y = gl->onoffrect.y + (gl->onoffrect.height-gl->onoffinner.height)/2;
743 }
744 
_GCheckBoxCreate(GCheckBox * gl,struct gwindow * base,GGadgetData * gd,void * data,GBox * def)745 static GCheckBox *_GCheckBoxCreate(GCheckBox *gl, struct gwindow *base, GGadgetData *gd,void *data, GBox *def) {
746 
747     if ( !gradio_inited )
748 	GRadioInit();
749     gl->g.funcs = &gradio_funcs;
750     _GGadget_Create(&gl->g,base,gd,data,def);
751 
752     gl->g.takes_input = true; gl->g.takes_keyboard = true; gl->g.focusable = true;
753     gl->font = checkbox_font;
754     if ( gd->label!=NULL ) {
755 	gl->image_precedes = gd->label->image_precedes;
756 	if ( gd->label->font!=NULL )
757 	    gl->font = gd->label->font;
758 	if ( gd->label->text_in_resource && gd->label->text_is_1byte )
759 	    gl->label = utf82u_mncopy((char *) gd->label->text,&gl->g.mnemonic);
760 	else if ( gd->label->text_in_resource )
761 	    gl->label = u_copy((unichar_t *) GStringGetResource((intpt) gd->label->text,&gl->g.mnemonic));
762 	else if ( gd->label->text_is_1byte )
763 	    gl->label = /*def2u*/ utf82u_copy((char *) gd->label->text);
764 	else
765 	    gl->label = u_copy(gd->label->text);
766 	gl->image = gd->label->image;
767     }
768     if ( gd->flags & gg_cb_on )
769 	gl->ison = true;
770     if ( gl->isradio ) {
771 	gl->onbox = &radio_on_box;
772 	gl->offbox = &radio_off_box;
773 	gl->on = radon;
774 	gl->off = radoff;
775 	gl->ondis = raddison;
776 	gl->offdis = raddisoff;
777     } else {
778 	gl->onbox = &checkbox_on_box;
779 	gl->offbox = &checkbox_off_box;
780 	gl->on = checkon;
781 	gl->off = checkoff;
782 	gl->ondis = checkdison;
783 	gl->offdis = checkdisoff;
784     }
785     GCheckBoxFit(gl);
786     _GGadget_FinalPosition(&gl->g,base,gd);
787 
788     if ( gd->flags & gg_group_end )
789 	_GGadgetCloseGroup(&gl->g);
790 return( gl );
791 }
792 
GCheckBoxCreate(struct gwindow * base,GGadgetData * gd,void * data)793 GGadget *GCheckBoxCreate(struct gwindow *base, GGadgetData *gd,void *data) {
794     GCheckBox *gl = _GCheckBoxCreate(calloc(1,sizeof(GCheckBox)),base,gd,data,&checkbox_box);
795 
796 return( &gl->g );
797 }
798 
GVisibilityBoxCreate(struct gwindow * base,GGadgetData * gd,void * data)799 GGadget *GVisibilityBoxCreate(struct gwindow *base, GGadgetData *gd,void *data) {
800     GCheckBox *gl = _GCheckBoxCreate(calloc(1,sizeof(GCheckBox)),base,gd,data,&checkbox_box);
801     gl->onbox = &visibility_on_box;
802     gl->offbox = &visibility_off_box;
803     gl->on = visibilityon;
804     gl->off = visibilityoff;
805     gl->ondis = visibilitydison;
806     gl->offdis = visibilitydisoff;
807 
808 return( &gl->g );
809 }
810 
GRadioCreate(struct gwindow * base,GGadgetData * gd,void * data)811 GGadget *GRadioCreate(struct gwindow *base, GGadgetData *gd,void *data) {
812     GRadio *gl = (GRadio *) calloc(1,sizeof(GRadio));
813     GGadget *gr;
814 
815     gl->isradio = true;
816     gl->radiogroup = gd->u.radiogroup;
817     _GCheckBoxCreate((GCheckBox *) gl,base,gd,data,&radio_box);
818 
819     gl->post = gl;
820     if ( gd->flags & gg_rad_startnew )
821 	/* Done */;
822     else if ( gl->g.prev!=NULL && gl->radiogroup!=0 ) {
823 	for ( gr=gl->g.prev; gr!=NULL; gr = gr->prev ) {
824 	    if ( gr->funcs==&gradio_funcs && ((GRadio *) gr)->radiogroup == gl->radiogroup ) {
825 		gl->post = ((GRadio *) gr)->post;
826 		((GRadio *) gr)->post = gl;
827 	break;
828 	    }
829 	}
830     } else if ( gl->g.prev!=NULL && gl->g.prev->funcs==&gradio_funcs &&
831 	    ((GRadio *) (gl->g.prev))->isradio ) {
832 	gl->post = ((GRadio *) (gl->g.prev))->post;
833 	((GRadio *) (gl->g.prev))->post = gl;
834     } else if ( gd->flags & gg_rad_continueold ) {
835 	for ( gr=gl->g.prev; gr!=NULL && (gr->funcs!=&gradio_funcs ||
836 		!((GRadio *) gr)->isradio); gr = gr->prev );
837 	if ( gr!=NULL ) {
838 	    gl->post = ((GRadio *) gr)->post;
839 	    ((GRadio *) gr)->post = gl;
840 	}
841     }
842 
843 return( &gl->g );
844 }
845 
GGadgetSetChecked(GGadget * g,int ison)846 void GGadgetSetChecked(GGadget *g, int ison) {
847     GRadio *gr = (GRadio *) g;
848 
849     if ( gr->isradio ) {
850 	if ( ison && !gr->ison ) {
851 	    GRadio *other;
852 	    for ( other=gr->post; other!=gr; other = other->post ) {
853 		if ( other->ison ) {
854 		    other->ison = false;
855 		    _ggadget_redraw((GGadget *) other);
856 		}
857 	    }
858 	}
859     }
860     gr->ison = ison?1:0;
861     _ggadget_redraw(g);
862 }
863 
GGadgetIsChecked(GGadget * g)864 int GGadgetIsChecked(GGadget *g) {
865 return( ((GRadio *) g)->ison );
866 }
867 
_GRadioRIHead(void)868 GResInfo *_GRadioRIHead(void) {
869 
870     if ( !gradio_inited )
871 	GRadioInit();
872 return( &gradio_ri );
873 }
874