1 /* Copyright (C) 2006-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 
33 /* If the locale is hebrew or arabic should we lay out boxes right to left???*/
34 
35 #define GG_Glue		((GGadget *) -1)	/* Special entries */
36 #define GG_ColSpan	((GGadget *) -2)	/* for box elements */
37 #define GG_RowSpan	((GGadget *) -3)	/* Must match those in ggadget.h */
38 #define GG_HPad10	((GGadget *) -4)
39 
40 static GBox hvgroup_box = GBOX_EMPTY; /* Don't initialize here */
41 static GBox hvbox_box = GBOX_EMPTY; /* Don't initialize here */
42 static int ghvbox_inited = false;
43 
44 GResInfo ghvgroupbox_ri = {
45     NULL, &ggadget_ri, NULL, NULL,
46     &hvgroup_box,
47     NULL,
48     NULL,
49     NULL,
50     N_("HV Group Box"),
51     N_("A box drawn around other gadgets"),
52     "GGroup",
53     "Gdraw",
54     false,
55     omf_border_type|omf_border_shape|omf_padding|omf_main_background|omf_disabled_background,
56     NULL,
57     GBOX_EMPTY,
58     NULL,
59     NULL,
60     NULL
61 };
62 
_GHVBox_Init(void)63 static void _GHVBox_Init(void) {
64     if ( ghvbox_inited )
65 return;
66     _GGadgetCopyDefaultBox(&hvgroup_box);
67     _GGadgetCopyDefaultBox(&hvbox_box);
68     hvgroup_box.border_type = bt_engraved;
69     hvbox_box.border_type = bt_none;
70     hvbox_box.border_width = 0;
71     hvgroup_box.border_shape = hvbox_box.border_shape = bs_rect;
72     hvgroup_box.padding = 2;
73     hvbox_box.padding = 0;
74     /*hvgroup_box.flags = hvbox_box.flags = 0;*/
75     hvgroup_box.main_background = COLOR_TRANSPARENT;
76     hvgroup_box.disabled_background = COLOR_TRANSPARENT;
77     _GGadgetInitDefaultBox("GHVBox.",&hvbox_box,NULL);
78     _GGadgetInitDefaultBox("GGroup.",&hvgroup_box,NULL);
79     ghvbox_inited = true;
80 }
81 
GHVBox_destroy(GGadget * g)82 static void GHVBox_destroy(GGadget *g) {
83     GHVBox *gb = (GHVBox *) g;
84     int i;
85 
86     if ( gb->label != NULL )
87 	GGadgetDestroy( gb->label );
88     for ( i=0; i<gb->rows*gb->cols; ++i )
89 	if ( gb->children[i]!=GG_Glue && gb->children[i]!=GG_ColSpan &&
90 		gb->children[i]!=GG_RowSpan && gb->children[i]!=GG_HPad10 )
91 	    GGadgetDestroy(gb->children[i]);
92     free(gb->children);
93     _ggadget_destroy(g);
94 }
95 
GHVBoxMove(GGadget * g,int32 x,int32 y)96 static void GHVBoxMove(GGadget *g, int32 x, int32 y) {
97     GHVBox *gb = (GHVBox *) g;
98     int offx = x-g->r.x, offy = y-g->r.y;
99     int i;
100 
101     if ( gb->label!=NULL )
102 	GGadgetMove(gb->label,gb->label->inner.x+offx,gb->label->inner.y+offy);
103     for ( i=0; i<gb->rows*gb->cols; ++i )
104 	if ( gb->children[i]!=GG_Glue && gb->children[i]!=GG_ColSpan &&
105 		gb->children[i]!=GG_RowSpan && gb->children[i]!=GG_HPad10 )
106 	    GGadgetMove(gb->children[i],
107 		    gb->children[i]->r.x+offx, gb->children[i]->r.y+offy);
108     _ggadget_move(g,x,y);
109 }
110 
111 struct sizedata {
112     int extra_space;		/* a default button has "extra_space" */
113     int min;			/* This value includes padding */
114     int sized;
115     int allocated;
116     int allglue;
117 };
118 
119 struct sizeinfo {
120     struct sizedata *cols;
121     struct sizedata *rows;
122     int label_height, label_width;
123     int width, height;
124     int minwidth, minheight;
125 };
126 
GHVBoxGatherSizeInfo(GHVBox * gb,struct sizeinfo * si)127 static void GHVBoxGatherSizeInfo(GHVBox *gb,struct sizeinfo *si) {
128     int i,c,r,spanc, spanr, totc,totr, max, plus, extra, es;
129     GRect outer;
130     int ten = GDrawPointsToPixels(gb->g.base,10);
131 
132     memset(si,0,sizeof(*si));
133     si->cols = calloc(gb->cols,sizeof(struct sizedata));
134     si->rows = calloc(gb->rows,sizeof(struct sizedata));
135     for ( c=0; c<gb->cols; ++c ) si->cols[c].allglue = true;
136     for ( r=0; r<gb->rows; ++r ) si->rows[r].allglue = true;
137 
138     for ( r=0; r<gb->rows; ++r ) {
139 	for ( c=0; c<gb->cols; ++c ) {
140 	    GGadget *g = gb->children[r*gb->cols+c];
141 	    if ( g==GG_Glue ) {
142 		if ( c+1!=gb->cols && si->cols[c].min<gb->hpad ) si->cols[c].min = gb->hpad;
143 		if ( r+1!=gb->rows && si->rows[r].min<gb->vpad ) si->rows[r].min = gb->vpad;
144 	    } else if ( g==GG_HPad10 ) {
145 		if ( c+1!=gb->cols && si->cols[c].min<gb->hpad+ten ) si->cols[c].min = gb->hpad+ten;
146 		if ( r+1!=gb->rows && si->rows[r].min<gb->vpad ) si->rows[r].min = gb->vpad+ten/2;
147 		si->cols[c].allglue = false;
148 	    } else if ( g==GG_ColSpan || g==GG_RowSpan )
149 		/* Skip it */;
150 	    else if ( (r+1<gb->rows && gb->children[(r+1)*gb->cols+c]==GG_RowSpan) ||
151 		      (c+1<gb->cols && gb->children[r*gb->cols+c+1]==GG_ColSpan))
152 		/* This gadget spans some columns or rows. Come back later */;
153 	    else {
154 		GGadgetGetDesiredSize(g,&outer,NULL);
155 		es = GBoxExtraSpace(g);
156 		if ( c+1!=gb->cols && g->state!=gs_invisible )
157 		    outer.width += gb->hpad;
158 		if ( r+1!=gb->rows && g->state!=gs_invisible )
159 		    outer.height += gb->vpad;
160 		si->cols[c].allglue = false;
161 		if ( si->cols[c].extra_space<es ) si->cols[c].extra_space=es;
162 		if ( si->cols[c].min<outer.width ) si->cols[c].min=outer.width;
163 		si->rows[r].allglue = false;
164 		if ( si->rows[r].min<outer.height ) si->rows[r].min=outer.height;
165 		if ( si->rows[r].extra_space<es ) si->rows[r].extra_space=es;
166 	    }
167 	}
168     }
169 
170     for ( r=0; r<gb->rows; ++r ) {
171 	for ( c=0; c<gb->cols; ++c ) {
172 	    GGadget *g = gb->children[r*gb->cols+c];
173 	    if ( g==GG_Glue || g==GG_ColSpan || g==GG_RowSpan || g==GG_HPad10 )
174 		/* Skip it */;
175 	    else if ( (r+1<gb->rows && gb->children[(r+1)*gb->cols+c]==GG_RowSpan) ||
176 		      (c+1<gb->cols && gb->children[r*gb->cols+c+1]==GG_ColSpan)) {
177 		si->rows[r].allglue = false;
178 		totr = si->rows[r].min;
179 		for ( spanr=1; r+spanr<gb->rows &&
180 			gb->children[(r+spanr)*gb->cols+c]==GG_RowSpan; ++spanr ) {
181 		    si->rows[r+spanr].allglue = false;
182 		    totr += si->rows[r+spanr].min;
183 		}
184 		si->cols[c].allglue = false;
185 		totc = si->cols[c].min;
186 		for ( spanc=1; c+spanc<gb->cols &&
187 			gb->children[r*gb->cols+c+spanc]==GG_ColSpan; ++spanc ) {
188 		    si->cols[c+spanc].allglue = false;
189 		    totc += si->cols[c+spanc].min;
190 		}
191 		GGadgetGetDesiredSize(g,&outer,NULL);
192 		es = GBoxExtraSpace(g);
193 		if ( c+spanc!=gb->cols && g->state!=gs_invisible )
194 		    outer.width += gb->hpad;
195 		if ( r+spanr!=gb->rows && g->state!=gs_invisible )
196 		    outer.height += gb->vpad;
197 		if ( outer.width>totc ) {
198 		    plus = (outer.width-totc)/spanc;
199 		    extra = (outer.width-totc-spanc*plus);
200 		    for ( i=0; i<spanc; ++i ) {
201 			si->cols[c+i].min += plus + (extra>0);
202 			--extra;
203 		    }
204 		}
205 		if ( outer.height>totr ) {
206 		    plus = (outer.height-totr)/spanr;
207 		    extra = (outer.height-totr-spanr*plus);
208 		    for ( i=0; i<spanr; ++i ) {
209 			si->rows[r+i].min += plus + (extra>0);
210 			--extra;
211 		    }
212 		}
213 		if ( es!=0 ) {
214 		    for ( i=0; i<spanc; ++i ) {
215 			if ( es>si->cols[c+i].extra_space )
216 			    si->cols[c+i].extra_space = es;
217 		    }
218 		    for ( i=0; i<spanr; ++i ) {
219 			if ( es>si->rows[r+i].extra_space )
220 			    si->rows[r+i].extra_space = es;
221 		    }
222 		}
223 	    }
224 	}
225     }
226 
227     if ( gb->label!=NULL ) {
228 	GGadgetGetDesiredSize(gb->label,&outer,NULL);
229 	totc = 0;
230 	for ( c=0; c<gb->cols ; ++c )
231 	    totc += si->cols[c].min;
232 	outer.width += 20;		/* Set back on each side */
233 	if ( outer.width>totc ) {
234 	    plus = (outer.width-totc)/gb->cols;
235 	    extra = (outer.width-totc-gb->cols*plus);
236 	    for ( i=0; i<gb->cols; ++i ) {
237 		si->cols[i].min += plus + (extra>0);
238 		--extra;
239 	    }
240 	}
241 	si->label_height = outer.height;
242 	si->label_width = outer.width;
243     }
244 
245     for ( max=c=0; c<gb->cols; ++c )
246 	si->cols[c].sized = si->cols[c].min;
247     for ( max=r=0; r<gb->rows; ++r )
248 	si->rows[r].sized = si->rows[r].min;
249 
250     if ( gb->grow_col==gb_samesize ) {
251 	for ( max=c=0; c<gb->cols; ++c )
252 	    if ( max<si->cols[c].sized ) max = si->cols[c].sized;
253 	for ( c=0; c<gb->cols; ++c )
254 	    if ( si->cols[c].min!=0 || si->cols[c].allglue )
255 		si->cols[c].sized = max;
256     } else if ( gb->grow_col==gb_expandgluesame ) {
257 	for ( max=c=0; c<gb->cols; ++c )
258 	    if ( max<si->cols[c].sized && !si->cols[c].allglue ) max = si->cols[c].sized;
259 	for ( c=0; c<gb->cols; ++c )
260 	    if ( !si->cols[c].allglue && si->cols[c].min!=0 )	/* Must have at least one visible element */
261 		si->cols[c].sized = max;
262     }
263 
264     if ( gb->grow_row==gb_samesize ) {
265 	for ( max=r=0; r<gb->rows; ++r )
266 	    if ( max<si->rows[r].sized ) max = si->rows[r].sized;
267 	for ( r=0; r<gb->rows; ++r )
268 	    if ( si->rows[r].min!=0 || si->rows[r].allglue )
269 		si->rows[r].sized = max;
270     } else if ( gb->grow_row==gb_expandgluesame ) {
271 	for ( max=r=0; r<gb->rows; ++r )
272 	    if ( max<si->rows[r].sized && !si->rows[r].allglue ) max = si->rows[r].sized;
273 	for ( r=0; r<gb->rows; ++r )
274 	    if ( !si->rows[r].allglue && si->rows[r].min>0 )
275 		si->rows[r].sized = max;
276     }
277 
278     for ( i=si->width = si->minwidth = 0; i<gb->cols; ++i ) {
279 	si->width += si->cols[i].sized;
280 	si->minwidth += si->cols[i].min;
281     }
282     for ( i=0, si->height=si->minheight = si->label_height; i<gb->rows; ++i ) {
283 	si->height += si->rows[i].sized;
284 	si->minheight += si->rows[i].min;
285     }
286 }
287 
GHVBoxResize(GGadget * g,int32 width,int32 height)288 static void GHVBoxResize(GGadget *g, int32 width, int32 height) {
289     struct sizeinfo si;
290     GHVBox *gb = (GHVBox *) g;
291     int bp = GBoxBorderWidth(g->base,g->box);
292     int i,c,r,spanc, spanr, totc,totr, glue_cnt, plus, extra;
293     int x,y;
294     int old_enabled = GDrawEnableExposeRequests(g->base,false);
295 
296     GHVBoxGatherSizeInfo(gb,&si);
297     width -= 2*bp; height -= 2*bp;
298 
299     if(width < si.minwidth) width = si.minwidth;
300     if(height < si.minheight) height = si.minheight;
301 
302     gb->g.inner.x = gb->g.r.x + bp; gb->g.inner.y = gb->g.r.y + bp;
303     if ( gb->label!=NULL ) {
304         gb->label_height = si.label_height;
305         g->inner.y += si.label_height;
306     }
307 
308     if ( si.width!=width ) {
309         int vcols=0;
310         for ( i=0; i<gb->cols-1; ++i )
311             if ( si.cols[i].sized>0 )
312                 ++vcols;
313         int vcols1 = vcols;
314         if(si.cols[gb->cols-1].sized > 0)
315             ++ vcols1;
316         if ( width<si.width ) {
317             for ( i=0; i<gb->cols; ++i )
318                 si.cols[i].sized = si.cols[i].min;
319             si.width = si.minwidth;
320             if ( width<si.width && gb->hpad>1 && vcols>0 ) {
321                 int reduce_pad = (si.width-width)/vcols + 1;
322                 if ( reduce_pad>gb->hpad-1 ) reduce_pad = gb->hpad-1;
323                 for ( i=0; i<gb->cols-1; ++i )
324                     if ( si.cols[i].sized > 0 )
325                         si.cols[i].sized -= reduce_pad;
326                 si.width -= vcols*reduce_pad;
327             }
328         }
329         if((width > si.width) && (gb->grow_col==gb_expandglue || gb->grow_col==gb_expandgluesame )) {
330             for ( i=glue_cnt=0; i<gb->cols; ++i )
331                 if ( si.cols[i].allglue )
332                     ++glue_cnt;
333             if ( glue_cnt!=0 ) {
334                 plus = (width-si.width)/glue_cnt;
335                 extra = (width-si.width-glue_cnt*plus);
336                 for ( i=0; i<gb->cols; ++i ) if ( si.cols[i].allglue ) {
337                     si.cols[i].sized += plus + (extra>0);
338                     si.width += plus + (extra>0);
339                     --extra;
340                 }
341             }
342         }
343         if ((width != si.width) && gb->grow_col>=0 ) {
344             int * ss = &(si.cols[gb->grow_col].sized);
345             int w = si.width - *ss;
346             *ss += (width-si.width);
347             if(*ss < gb->hpad + 3)
348                 *ss = gb->hpad + 3;
349             si.width = w + *ss;
350         }
351         if ((width > si.width) && (vcols1!=0)) {
352             plus = (width-si.width)/vcols1;
353             extra = (width-si.width-vcols1*plus);
354             for ( i=0; i<gb->cols; ++i ) {
355                 if ( si.cols[i].sized>0 ) {
356                     si.cols[i].sized += plus + (extra>0);
357                     si.width += plus + (extra>0);
358                     --extra;
359                 }
360             }
361         }
362         width = si.width;
363     }
364 
365     if ( si.height!=height ) {
366         int vrows=0;
367         for ( i=0; i<gb->rows-1; ++i )
368             if ( si.rows[i].sized>0 )
369                 ++vrows;
370         int vrows1 = vrows;
371         if(si.rows[gb->rows-1].sized > 0)
372             ++ vrows1;
373         if ( height<si.height ) {
374             for ( i=0; i<gb->rows; ++i )
375                 si.rows[i].sized = si.rows[i].min;
376             si.height = si.minheight;
377             if ( height<si.height && gb->vpad>1 && vrows>0 ) {
378                 int reduce_pad = (si.height-height)/vrows + 1;
379                 if ( reduce_pad>gb->vpad-1 ) reduce_pad = gb->vpad-1;
380                 for ( i=0; i<gb->rows-1; ++i )
381                     if ( si.rows[i].sized > 0 )
382                         si.rows[i].sized -= reduce_pad;
383                 si.height -= vrows*reduce_pad;
384             }
385         }
386         if((height > si.height) && (gb->grow_row==gb_expandglue || gb->grow_row==gb_expandgluesame )) {
387             for ( i=glue_cnt=0; i<gb->rows; ++i )
388                 if ( si.rows[i].allglue )
389                     ++glue_cnt;
390             if ( glue_cnt!=0 ){
391                 plus = (height-si.height)/glue_cnt;
392                 extra = (height-si.height-glue_cnt*plus);
393                 for ( i=0; i<gb->rows; ++i ) if ( si.rows[i].allglue ) {
394                     si.rows[i].sized += plus + (extra>0);
395                     si.height += plus + (extra>0);
396                     --extra;
397                 }
398             }
399         }
400         if ((height != si.height) && gb->grow_row>=0 ) {
401             int * ss = &(si.rows[gb->grow_row].sized);
402             int h = si.height - *ss;
403             *ss += (height-si.height);
404             if(*ss < gb->vpad + 3)
405                 *ss = gb->vpad + 3;
406             si.height = h + *ss;
407         }
408         if ((height > si.height) && (vrows1!=0)) {
409             plus = (height-si.height)/vrows1;
410             extra = (height-si.height-vrows1*plus);
411             for ( i=0; i<gb->rows; ++i ) {
412                 if ( si.rows[i].sized>0 ) {
413                     si.rows[i].sized += plus + (extra>0);
414                     si.height += plus + (extra>0);
415                     --extra;
416                 }
417             }
418         }
419         height = si.height;
420     }
421 
422     y = gb->g.inner.y;
423     if ( gb->label!=NULL ) {
424         if ( gb->label->state!=gs_invisible )
425             GGadgetResize( gb->label, si.label_width, si.label_height);
426         GGadgetMove( gb->label, gb->g.inner.x+10, y-si.label_height-bp/2);
427     }
428     for ( r=0; r<gb->rows; ++r ) {
429         x = gb->g.inner.x;
430         for ( c=0; c<gb->cols; ++c ) {
431             GGadget *g = gb->children[r*gb->cols+c];
432             if ( g==GG_Glue || g==GG_ColSpan || g==GG_RowSpan || g==GG_HPad10 )
433                 /* Skip it */;
434             else {
435                 int xes, yes, es;
436                 totr = si.rows[r].sized;
437                 for ( spanr=1; r+spanr<gb->rows &&
438                         gb->children[(r+spanr)*gb->cols+c]==GG_RowSpan; ++spanr )
439                     totr += si.rows[r+spanr].sized;
440                 totc = si.cols[c].sized;
441                 for ( spanc=1; c+spanc<gb->cols &&
442                         gb->children[r*gb->cols+c+spanc]==GG_ColSpan; ++spanc )
443                     totc += si.cols[c+spanc].sized;
444                 if ( r+spanr!=gb->rows ) totr -= gb->vpad;
445                 if ( c+spanc!=gb->cols ) totc -= gb->hpad;
446                 es = GBoxExtraSpace(g);
447                 xes = si.cols[c].extra_space - es;
448                 yes = si.rows[r].extra_space - es;
449                 if ( g->state!=gs_invisible )
450                     GGadgetResize(g,totc-2*xes,totr-2*yes);
451                 GGadgetMove(g,x+xes,y+yes);
452             }
453             x += si.cols[c].sized;
454         }
455         y += si.rows[r].sized;
456     }
457 
458     free(si.cols); free(si.rows);
459 
460     gb->g.inner.width = width; gb->g.inner.height = height;
461     gb->g.r.width = width + 2*bp; gb->g.r.height = height + 2*bp;
462     GDrawEnableExposeRequests(g->base,old_enabled);
463     GDrawRequestExpose(g->base,&g->r,false);
464 }
465 
GHVBoxGetDesiredSize(GGadget * g,GRect * outer,GRect * inner)466 static void GHVBoxGetDesiredSize(GGadget *g, GRect *outer, GRect *inner) {
467     struct sizeinfo si;
468     GHVBox *gb = (GHVBox *) g;
469     int bp = GBoxBorderWidth(g->base,g->box);
470 
471     GHVBoxGatherSizeInfo(gb,&si);
472     if ( g->desired_width>0 ) si.width = g->desired_width;
473     if ( g->desired_height>0 ) si.height = g->desired_height;
474 
475     if ( inner!=NULL ) {
476 	inner->x = inner->y = 0;
477 	inner->width = si.width; inner->height = si.height;
478     }
479     if ( outer!=NULL ) {
480 	outer->x = outer->y = 0;
481 	outer->width = si.width+2*bp; outer->height = si.height+2*bp;
482     }
483     free(si.cols); free(si.rows);
484 }
485 
GHVBoxFillsWindow(GGadget * g)486 static int GHVBoxFillsWindow(GGadget *g) {
487 return( true );
488 }
489 
expose_nothing(GWindow pixmap,GGadget * g,GEvent * event)490 static int expose_nothing(GWindow pixmap, GGadget *g, GEvent *event) {
491     GHVBox *gb = (GHVBox *) g;
492     GRect r;
493 
494     if ( g->state==gs_invisible )
495 return( true );
496 
497     if ( gb->label==NULL )
498 	GBoxDrawBorder(pixmap,&g->r,g->box,g->state,false);
499     else {
500 	r = g->r;
501 	r.y += gb->label_height/2;
502 	r.height -= gb->label_height/2;
503 	GBoxDrawBorder(pixmap,&r,g->box,g->state,false);
504 	/* Background is transperant */
505 	(gb->label->funcs->handle_expose)(pixmap,gb->label,event);
506     }
507 return( true );
508 }
509 
510 struct gfuncs ghvbox_funcs = {
511     0,
512     sizeof(struct gfuncs),
513 
514     expose_nothing,	/* Expose */
515     _ggadget_noop,	/* Mouse */
516     _ggadget_noop,	/* Key */
517     NULL,
518     NULL,		/* Focus */
519     NULL,
520     NULL,
521 
522     _ggadget_redraw,
523     GHVBoxMove,
524     GHVBoxResize,
525     _ggadget_setvisible,
526     _ggadget_setenabled,
527     _ggadget_getsize,
528     _ggadget_getinnersize,
529 
530     GHVBox_destroy,
531 
532     NULL,
533     NULL,
534     NULL,
535     NULL,
536     NULL,
537 
538     NULL,
539     NULL,
540 
541     NULL,
542     NULL,
543     NULL,
544     NULL,
545     NULL,
546     NULL,
547     NULL,
548     NULL,
549     NULL,
550     NULL,
551     NULL,
552 
553     GHVBoxGetDesiredSize,
554     _ggadget_setDesiredSize,
555     GHVBoxFillsWindow,
556     NULL
557 };
558 
GHVBoxSetExpandableCol(GGadget * g,int col)559 void GHVBoxSetExpandableCol(GGadget *g,int col) {
560     GHVBox *gb = (GHVBox *) g;
561     if ( col<gb->cols )
562 	gb->grow_col = col;
563 }
564 
GHVBoxSetExpandableRow(GGadget * g,int row)565 void GHVBoxSetExpandableRow(GGadget *g,int row) {
566     GHVBox *gb = (GHVBox *) g;
567     if ( row < gb->rows )
568 	gb->grow_row = row;
569 }
570 
GHVBoxSetPadding(GGadget * g,int hpad,int vpad)571 void GHVBoxSetPadding(GGadget *g,int hpad, int vpad) {
572     GHVBox *gb = (GHVBox *) g;
573     gb->hpad = hpad;
574     gb->vpad = vpad;
575 }
576 
_GHVBoxCreate(struct gwindow * base,GGadgetData * gd,void * data,int hcnt,int vcnt,GBox * def_box)577 static GHVBox *_GHVBoxCreate(struct gwindow *base, GGadgetData *gd,void *data,
578 	int hcnt, int vcnt, GBox *def_box) {
579     GHVBox *gb = calloc(1,sizeof(GHVBox));
580     int i, h, v;
581     GGadgetCreateData *label = (GGadgetCreateData *) (gd->label);
582 
583     if ( !ghvbox_inited )
584 	_GHVBox_Init();
585 
586     gd->label = NULL;
587     gb->g.funcs = &ghvbox_funcs;
588     _GGadget_Create(&gb->g,base,gd,data,def_box);
589     gb->rows = vcnt; gb->cols = hcnt;
590     gb->grow_col = gb->grow_row = gb_expandall;
591     gb->hpad = gb->vpad = GDrawPointsToPixels(base,2);
592 
593     gb->g.takes_input = false; gb->g.takes_keyboard = false; gb->g.focusable = false;
594 
595     if ( label != NULL ) {
596 	gb->label = label->ret =
597 		(label->creator)(base,&label->gd,label->data);
598 	gb->label->contained = true;
599     }
600 
601     gb->children = malloc(vcnt*hcnt*sizeof(GGadget *));
602     for ( i=v=0; v<vcnt; ++v ) {
603 	for ( h=0; h<hcnt && gd->u.boxelements[i]!=NULL; ++h, ++i ) {
604 	    GGadgetCreateData *gcd = gd->u.boxelements[i];
605 	    if ( gcd==GCD_Glue ||
606 		    gcd==GCD_ColSpan ||
607 		    gcd==GCD_RowSpan ||
608 		    gcd==GCD_HPad10 )
609 		gb->children[v*hcnt+h] = (GGadget *) gcd;
610 	    else {
611 		gcd->gd.pos.x = gcd->gd.pos.y = 1;
612 		gb->children[v*hcnt+h] = gcd->ret =
613 			(gcd->creator)(base,&gcd->gd,gcd->data);
614 		gcd->ret->contained = true;
615 	    }
616 	}
617 	while ( h<hcnt )
618 	    gb->children[v*hcnt+h++] = GG_Glue;
619 	if ( gd->u.boxelements[i]==NULL )
620 	    ++i;
621     }
622 return( gb );
623 }
624 
GHBoxCreate(struct gwindow * base,GGadgetData * gd,void * data)625 GGadget *GHBoxCreate(struct gwindow *base, GGadgetData *gd,void *data) {
626     GHVBox *gb;
627     int hcnt;
628 
629     for ( hcnt=0; gd->u.boxelements[hcnt]!=NULL; ++hcnt );
630     gb = _GHVBoxCreate(base,gd,data,hcnt,1,&hvbox_box);
631 
632 return( &gb->g );
633 }
634 
GVBoxCreate(struct gwindow * base,GGadgetData * gd,void * data)635 GGadget *GVBoxCreate(struct gwindow *base, GGadgetData *gd,void *data) {
636     GHVBox *gb;
637     int vcnt;
638 
639     for ( vcnt=0; gd->u.boxelements[vcnt]!=NULL; ++vcnt );
640     gb = _GHVBoxCreate(base,gd,data,1,vcnt,&hvbox_box);
641 
642 return( &gb->g );
643 }
644 
GHVBoxCreate(struct gwindow * base,GGadgetData * gd,void * data)645 GGadget *GHVBoxCreate(struct gwindow *base, GGadgetData *gd,void *data) {
646     GHVBox *gb;
647     int hcnt, vcnt, i;
648 
649     for ( hcnt=0; gd->u.boxelements[hcnt]!=NULL; ++hcnt );
650     for ( i=0, vcnt=1; gd->u.boxelements[i]!=NULL || gd->u.boxelements[i+1]!=NULL ; ++i )
651 	if ( gd->u.boxelements[i]==NULL )
652 	    ++vcnt;
653     gb = _GHVBoxCreate(base,gd,data,hcnt,vcnt,&hvbox_box);
654 
655 return( &gb->g );
656 }
657 
GHVGroupCreate(struct gwindow * base,GGadgetData * gd,void * data)658 GGadget *GHVGroupCreate(struct gwindow *base, GGadgetData *gd,void *data) {
659     GHVBox *gb;
660     int hcnt, vcnt, i;
661 
662     for ( hcnt=0; gd->u.boxelements[hcnt]!=NULL; ++hcnt );
663     for ( i=0, vcnt=1; gd->u.boxelements[i]!=NULL || gd->u.boxelements[i+1]!=NULL ; ++i )
664 	if ( gd->u.boxelements[i]==NULL )
665 	    ++vcnt;
666     gb = _GHVBoxCreate(base,gd,data,hcnt,vcnt,&hvgroup_box);
667 
668 return( &gb->g );
669 }
670 
_GHVBoxFitWindow(GGadget * g,int center)671 static void _GHVBoxFitWindow(GGadget *g, int center) {
672     GRect outer, cur, screen;
673 
674     if ( !GGadgetFillsWindow(g)) {
675 	fprintf( stderr, "Call to GHVBoxFitWindow in something not an HVBox\n" );
676 return;
677     }
678     GHVBoxGetDesiredSize(g,&outer, NULL );
679     GDrawGetSize(GDrawGetRoot(NULL),&screen);
680     if ( outer.width > screen.width-20 ) outer.width = screen.width-20;
681     if ( outer.height > screen.height-40 ) outer.height = screen.height-40;
682     GDrawGetSize(g->base,&cur);
683     /* Make any offset simmetrical */
684     outer.width += 2*g->r.x;
685     outer.height += 2*g->r.y;
686     if ( cur.width!=outer.width || cur.height!=outer.height ) {
687 	GDrawResize(g->base, outer.width, outer.height );
688 	/* We want to get the resize before we set the window visible */
689 	/*  and window managers make synchronizing an issue... */
690 	GDrawSync(GDrawGetDisplayOfWindow(g->base));
691 	GDrawProcessPendingEvents(GDrawGetDisplayOfWindow(g->base));
692 	GDrawSync(GDrawGetDisplayOfWindow(g->base));
693 	GDrawProcessPendingEvents(GDrawGetDisplayOfWindow(g->base));
694     } else
695 	GGadgetResize(g, outer.width-2*g->r.x, outer.height-2*g->r.y );
696     if ( center ) {
697 	GDrawMove(g->base,(screen.width-outer.width)/2,(screen.height-outer.height)/2);
698 	/* I don't think this one matters as much, but try a little */
699 	GDrawSync(GDrawGetDisplayOfWindow(g->base));
700 	GDrawProcessPendingEvents(GDrawGetDisplayOfWindow(g->base));
701     }
702 }
703 
GHVBoxFitWindow(GGadget * g)704 void GHVBoxFitWindow(GGadget *g) {
705     _GHVBoxFitWindow(g,false);
706 }
707 
GHVBoxFitWindowCentered(GGadget * g)708 void GHVBoxFitWindowCentered(GGadget *g) {
709     _GHVBoxFitWindow(g,true);
710 }
711 
GHVBoxReflow(GGadget * g)712 void GHVBoxReflow(GGadget *g) {
713     GHVBoxResize(g, g->r.width, g->r.height);
714 }
715 
_GHVBoxRIHead(void)716 GResInfo *_GHVBoxRIHead(void) {
717 
718     _GHVBox_Init();
719 return( &ghvgroupbox_ri );
720 }
721