1 /*    SCCS Id: @(#)wbgads.c     1.0   93/04/18			  */
2 /*    Copyright (c) Gregg Wonderly, Naperville IL, 1993	  	  */
3 /* NetHack may be freely redistributed.  See license for details. */
4 
5 /* wbgads.c */
6 static void GadSpaceOut(struct OPTGAD *gads , int row , int maxx);
7 static void CompSpaceOut(struct OPTGAD *gads , int row , int maxx);
8 
9 extern NEARDATA struct flag flags;
10 
11 #define INITX	7
12 #define GADBORD	3
13 #define	YSPACE	2
14 #define XSPACE	3
15 
16 #define MAXGADSTRLEN	2000
17 
18 static char undobuffer[ MAXGADSTRLEN + 1 ];
19 
20 static struct TextAttr textfont;
21 static int compgadid;
22 
23 char **compvals;
24 
25 #ifdef	TESTING
26 void SetBorder( register struct Gadget *gd, int val );
27 
main(argc,argv)28 main( argc, argv )
29     int argc;
30     char **argv;
31 {
32     int done = 0;
33     struct IntuiMessage *imsg;
34     struct Window *w;
35     struct Screen *screen;
36     int cury = -1, gadid = 1;
37     struct OPTGAD *gp, *boolgads, *compgads;
38 
39     screen = LockPubScreen( NULL );
40     UnlockPubScreen( NULL, screen );
41 
42     w = OpenWindowTags( NULL,
43 	WA_IDCMP, IDCMP_CLOSEWINDOW|IDCMP_NEWSIZE|IDCMP_SIZEVERIFY,
44 	WA_Flags, WFLG_DRAGBAR|WFLG_SIZEGADGET|WFLG_DEPTHGADGET|
45 		  WFLG_CLOSEGADGET,
46 	WA_SmartRefresh, 1,
47 	WA_NoCareRefresh, 1,
48 	WA_MinWidth, 100,
49 	WA_MinHeight, 50,
50 	WA_PubScreen, screen,
51 	WA_Width, 640,
52 	WA_Height, 400,
53 	WA_Top, 0,
54 	WA_Left, 0,
55 	TAG_DONE );
56 
57     if( w )
58     {
59 	textfont.ta_Name = w->RPort->Font->tf_Message.mn_Node.ln_Name;
60 	textfont.ta_YSize = w->RPort->Font->tf_YSize;
61 	textfont.ta_Style = w->RPort->Font->tf_Style;
62 	textfont.ta_Flags = w->RPort->Font->tf_Flags;
63 	boolgads = LayoutBoolOpts( w->Width - w->BorderLeft - w->BorderRight - 5,
64 			w->Height - w->BorderTop - w->BorderBottom - 6,
65 			w->RPort, &cury, &gadid );
66 	if( boolgads )
67 	{
68 	    for( gp = boolgads; gp; gp = gp->next )
69 		AddGList( w, &gp->gad, 0, 1, NULL );
70 	    RefreshGList( w->FirstGadget, w, NULL, -1 );
71 	}
72 	compgads = LayoutCompOpts( w->Width - w->BorderLeft - w->BorderRight - 5,
73 			w->Height - w->BorderTop - w->BorderBottom - 6, w->RPort,
74 			&cury, &gadid );
75 	if( compgads )
76 	{
77 	    for( gp = compgads; gp; gp = gp->next )
78 		AddGList( w, &gp->gad, 0, 1, NULL );
79 	    RefreshGList( w->FirstGadget, w, NULL, -1 );
80 	}
81     }
82 
83     while( !done && w )
84     {
85 	WaitPort( w->UserPort );
86 	while( imsg = (struct IntuiMessage *) GetMsg( w->UserPort ) )
87 	{
88 	    if( imsg->Class == CLOSEWINDOW )
89 		    done = 1;
90 	    else if( imsg->Class == SIZEVERIFY )
91 	    {
92 		for( gp = boolgads; gp; gp = gp->next )
93 		    RemoveGList( w, &gp->gad, 1 );
94 		FreeBoolOpts( boolgads );
95 		for( gp = compgads; gp; gp = gp->next )
96 		    RemoveGList( w, &gp->gad, 1 );
97 		FreeCompOpts( compgads );
98 	    }
99 	    else if( imsg->Class == NEWSIZE )
100 	    {
101 	    	cury = -1;
102 		gadid = 1;
103 		SetAPen( w->RPort, 0 );
104 		SetBPen( w->RPort, 0 );
105 		SetDrMd( w->RPort, JAM2 );
106 		RectFill( w->RPort, w->BorderLeft, w->BorderTop,
107 				    w->Width - w->BorderRight - 1,
108 				    w->Height - w->BorderBottom - 1 );
109 		SetAPen( w->RPort, 1 );
110 		SetBPen( w->RPort, 0 );
111 		boolgads = LayoutBoolOpts( w->Width - w->BorderLeft - w->BorderRight - 5,
112 		    w->Height - w->BorderTop - w->BorderBottom - 6, w->RPort,
113 			&cury, &gadid );
114 		for( gp = boolgads; gp; gp = gp->next )
115 		    AddGList( w, &gp->gad, 0, 1, NULL );
116 		compgads = LayoutCompOpts( w->Width - w->BorderLeft - w->BorderRight - 5,
117 		    w->Height - w->BorderTop - w->BorderBottom - 6, w->RPort,
118 			&cury, &gadid );
119 		for( gp = compgads; gp; gp = gp->next )
120 		    AddGList( w, &gp->gad, 0, 1, NULL );
121 		RefreshGList( w->FirstGadget, w, NULL, -1 );
122 	    }
123 
124 	    ReplyMsg( (struct Message *)imsg );
125 	}
126     }
127     if( w ) CloseWindow( w );
128     if( boolgads ) FreeBoolOpts( boolgads );
129     if( compgads ) FreeCompOpts( compgads );
130 
131     return( 0 );
132 }
133 
134 #endif
135 
AllocCompVals(void)136 int AllocCompVals( void )
137 {
138     int i;
139     if( compvals == 0 )
140     {
141 	for( i = 0; compopt[ i ].name; ++i )
142 	    continue;
143 	compvals = malloc( (i+1) * sizeof( char * ) );
144 	if( compvals == 0 )
145 	    return 0;
146 	for( i = 0; compopt[ i ].name; ++i )
147 	{
148 	    if( ( compvals[ i ] = malloc( compopt[ i ].size + 1 ) ) == NULL )
149 		return 0;
150 	    *compvals[ i ] = 0;
151 	}
152 
153 	/* Create a null pointer terminator */
154 	compvals[ i ] = 0;
155     }
156     return 1;
157 }
158 
159 struct OPTGAD *
LayoutCompOpts(maxx,maxy,rp,cury,gadid)160 LayoutCompOpts( maxx, maxy, rp, cury, gadid )
161     int maxx, maxy;
162     struct RastPort *rp;
163     int *cury;
164     int *gadid;
165 
166 {
167     int len;
168     struct OPTGAD *gp;
169     char *bp;
170     struct Gadget *gd;
171     struct StringInfo *sp;
172     struct OPTGAD *compgads;
173     struct IntuiText *ip;
174     int i, curx;
175     int incy;
176 
177     compgadid = *gadid;
178     incy = rp->TxHeight + GADBORD + YSPACE;
179 
180     compgads = 0;
181     curx = INITX;
182 
183     /* The value of 8 used here is not related to GADBORD!
184      * It is an estimate of screen->WBorTop + 1, with some
185      * extra white space for a border between the top
186      * of the window and the gadgets.
187      */
188 
189     textfont.ta_Name = rp->Font->tf_Message.mn_Node.ln_Name;
190     textfont.ta_YSize = rp->Font->tf_YSize;
191     textfont.ta_Style = rp->Font->tf_Style;
192     textfont.ta_Flags = rp->Font->tf_Flags;
193 
194     if( *cury == -1 )
195 	*cury = rp->TxHeight + 4;
196 
197     if( AllocCompVals() == 0 )
198     	return( NULL );
199 
200     for( i = 0; compopt[ i ].name; ++i )
201     {
202 	len = ( ( maxx - 20 - GADBORD ) / 2 ) + GADBORD;
203 
204 	if( curx + len > maxx )
205 	{
206 	    CompSpaceOut( compgads, *cury, maxx );
207 	    curx = INITX;
208 	    *cury += incy;
209 	}
210 
211 	gp = malloc( sizeof( *gp ) );
212 	ip = malloc( sizeof( *ip ) );
213 	sp = malloc( sizeof( *sp ) );
214 	bp = malloc( compopt[i].size + 1 );
215 	if( !gp || !ip || !sp || !bp )
216 	{
217 	    if( gp ) free( gp );
218 	    if( ip ) free( ip );
219 	    if( sp ) free( sp );
220 	    if( bp ) free( bp );
221 	    FreeCompOpts( compgads );
222 	    return( NULL );
223 	}
224 	memset( gp, 0, sizeof( *gp ) );
225 	memset( ip, 0, sizeof( *ip ) );
226 	memset( sp, 0, sizeof( *sp ) );
227 	gd = &gp->gad;
228 	gd->LeftEdge = curx + TextLength( rp, (char *)compopt[ i ].name,
229 			    strlen( (char *)compopt[ i ].name ) ) + 10;
230 	curx += len + GADBORD;
231 	gd->Width = len - (TextLength( rp, (char *)compopt[ i ].name,
232 			    strlen( (char *)compopt[ i ].name ) ) + 10 );
233 	gd->TopEdge = *cury;
234 	gd->Height = incy - (YSPACE * 2);
235 	gd->Flags = GADGHCOMP;
236 	gd->Activation = RELVERIFY;
237 	gd->GadgetType = STRGADGET;
238 	gd->GadgetRender = gd->SelectRender = 0;
239 	gd->GadgetText = ip;
240 	gd->MutualExclude = 0;
241 	gd->SpecialInfo = (APTR)sp;
242 
243 	sp->Buffer = bp;
244 	memcpy( bp, compvals[ i ], compopt[i].size );
245 	bp[ compopt[i].size ] = 0;
246 	sp->UndoBuffer = undobuffer;
247 	sp->MaxChars = compopt[i].size;
248 	gp->val = bp;
249 
250 	gd->GadgetID = (*gadid)++;
251 	gd->UserData = 0;
252 	gp->next = compgads;
253 	compgads = gp;
254 
255 	ip->FrontPen = 1;
256 	ip->BackPen = 0;
257 	ip->DrawMode = JAM1;
258 	ip->LeftEdge = -( TextLength( rp, (char *)compopt[ i ].name,
259 			    strlen( (char *)compopt[ i ].name ) ) + 10);
260 	ip->TopEdge = ((gd->Height - rp->TxHeight)/2);
261 	ip->ITextFont = &textfont;	/* rp->Font will be used */
262 	ip->IText = (char *)compopt[ i ].name;
263 	ip->NextText = 0;
264 	SetBorder( gd, 0 );
265     }
266 
267     /* Perhaps leave last row ragged? */
268     if( curx != INITX )
269 	CompSpaceOut( compgads, *cury, maxx );
270 
271     *cury += incy;
272     return( compgads );
273 }
274 
275 struct OPTGAD *
LayoutBoolOpts(maxx,maxy,rp,cury,gadid)276 LayoutBoolOpts( maxx, maxy, rp, cury, gadid )
277     int maxx, maxy;
278     struct RastPort *rp;
279     int *cury;
280     int *gadid;
281 {
282     int len;
283     struct OPTGAD *gp;
284     struct Gadget *gd;
285     struct OPTGAD *boolgads;
286     struct IntuiText *ip;
287     int i, curx;
288     int incy;
289 
290     incy = rp->TxHeight + GADBORD + YSPACE;
291 
292     boolgads = 0;
293     curx = INITX;
294 
295     /* The value of 5 used here is not related to GADBORD!
296      * It is an estimate of screen->WBorTop + 1, with some
297      * extra white space for a border are between the top
298      * of the window and the gadgets.
299      */
300 
301     if( *cury == -1 )
302 	*cury = rp->TxHeight + 5;
303 
304     textfont.ta_Name = rp->Font->tf_Message.mn_Node.ln_Name;
305     textfont.ta_YSize = rp->Font->tf_YSize;
306     textfont.ta_Style = rp->Font->tf_Style;
307     textfont.ta_Flags = rp->Font->tf_Flags;
308 
309     for( i = 0; boolopt[ i ].name; ++i )
310     {
311     	/* Null pointers indicate options which we do not have available to us */
312     	if( boolopt[ i ].addr == NULL )
313     	{
314 	    (*gadid)++;
315 	    continue;
316     	}
317 	len = TextLength( rp, (char *)boolopt[ i ].name,
318 			    strlen( (char *)boolopt[ i ].name ) );
319 
320 	if( curx + len > maxx )
321 	{
322 	    GadSpaceOut( boolgads, *cury, maxx );
323 	    curx = INITX;
324 	    *cury += incy;
325 	}
326 
327 	gp = malloc( sizeof( *gp ) );
328 	ip = malloc( sizeof( *ip ) );
329 	if( !gp || !ip )
330 	{
331 	    if( gp ) free( gp );
332 	    if( ip ) free( ip );
333 	    FreeBoolOpts( boolgads );
334 	    return( NULL );
335 	}
336 	memset( gp, 0, sizeof( *gp ) );
337 	memset( ip, 0, sizeof( *ip ) );
338 	gd = &gp->gad;
339 	gd->LeftEdge = curx;
340 	curx += len + GADBORD + XSPACE;
341 	gd->Width = len + GADBORD;
342 	gd->TopEdge = *cury;
343 	gd->Height = incy - YSPACE;
344 	gd->Flags = GFLG_GADGHCOMP;
345 	if( *boolopt[ i ].addr == TRUE )
346 	    gd->Flags |= GFLG_SELECTED;
347 	gd->Activation = GACT_IMMEDIATE|GACT_TOGGLESELECT|GACT_RELVERIFY;
348 	gd->GadgetType = GTYP_BOOLGADGET;
349 	gd->GadgetRender = gd->SelectRender = 0;
350 	gd->GadgetText = ip;
351 	gd->MutualExclude = 0;
352 	gd->SpecialInfo = 0;
353 	gd->GadgetID = (*gadid)++;
354 	gd->UserData = 0;
355 	gp->next = boolgads;
356 	boolgads = gp;
357 
358 	ip->FrontPen = 1;
359 	ip->BackPen = 0;
360 	ip->DrawMode = JAM1;
361 	ip->LeftEdge = (gd->Width - len)/2;
362 	ip->TopEdge = ((gd->Height - rp->TxHeight)/2) + 1;
363 	ip->ITextFont = &textfont;	/* rp->Font will be used */
364 	ip->IText = (char *)boolopt[ i ].name;
365 	ip->NextText = 0;
366 	SetBorder( gd, 1 );
367     }
368 
369     /* Perhaps leave last row ragged? */
370     if( curx != INITX )
371 	GadSpaceOut( boolgads, *cury, maxx );
372 
373     *cury += incy + GADBORD;
374     return( boolgads );
375 }
376 
377 static void
CompSpaceOut(gads,row,maxx)378 CompSpaceOut( gads, row, maxx )
379     struct OPTGAD *gads;
380     int row, maxx;
381 {
382     struct Gadget *gd;
383     struct OPTGAD *gp;
384     int cnt, tlen;
385 
386     tlen = cnt = 0;
387 
388     for( gp = gads; gp; gp = gp->next )
389     {
390 	if( gp->gad.TopEdge == row+1 )
391 	    ++cnt;
392     }
393 
394     if( cnt > 1)
395     {
396 	for( gp = gads; gp; gp = gp->next )
397 	{
398 	    gd = &gp->gad;
399 	    if( gd->TopEdge == row+1 && cnt != 0 )
400 	    {
401 		if( gd->LeftEdge > maxx/2 )
402 		    gd->LeftEdge += maxx - (gd->LeftEdge + gd->Width) + 2;
403 	    }
404 	}
405     }
406 }
407 
408 static void
GadSpaceOut(gads,row,maxx)409 GadSpaceOut( gads, row, maxx )
410     struct OPTGAD *gads;
411     int row, maxx;
412 {
413     struct Gadget *gd;
414     struct OPTGAD *gp;
415     int cnt, tlen, mod, inc;
416 
417     tlen = cnt = 0;
418 
419     for( gp = gads; gp; gp = gp->next )
420     {
421 	gd = &gp->gad;
422 	if( gd->TopEdge == row )
423 	{
424 	    ++cnt;
425 	    tlen += gd->Width + XSPACE;
426 	}
427     }
428 
429     if( tlen < maxx && cnt > 1)
430     {
431     	inc = ( maxx - tlen ) / (cnt-1);
432     	mod = ( maxx - tlen ) % (cnt-1);
433 
434 	for( gp = gads; gp; gp = gp->next )
435 	{
436 	    gd = &gp->gad;
437 	    if( gd->TopEdge == row && cnt != 0 )
438 	    {
439 		gd->LeftEdge += (inc * --cnt);
440 		if( mod )
441 		    gd->LeftEdge += --mod;
442 	    }
443 	}
444     }
445 }
446 
447 void
FreeCompOpts(compgads)448 FreeCompOpts( compgads )
449     register struct OPTGAD *compgads;
450 {
451     register struct OPTGAD *gp;
452     register struct IntuiText *ip;
453     struct StringInfo *sp;
454     struct Gadget *gd;
455 
456     while( gp = compgads )
457     {
458 	compgads = compgads->next;
459 	gd = &gp->gad;
460 
461 	if( ip = gd->GadgetText )
462 	{
463 	    if( ip->IText ) free( ip->IText );
464 	    free( ip );
465 	}
466 	if( sp = (struct StringInfo *)gd->SpecialInfo )
467 	{
468 	    if( sp->Buffer )
469 	    {
470 		memcpy( compvals[ gd->GadgetID - compgadid ], sp->Buffer, sp->MaxChars );
471 		free( sp->Buffer );
472 	    }
473 	    free( sp );
474 	}
475 	free( gp );
476     }
477 }
478 
479 void
FreeBoolOpts(boolgads)480 FreeBoolOpts( boolgads )
481     register struct OPTGAD *boolgads;
482 {
483     register struct OPTGAD *gp;
484     register struct IntuiText *ip;
485 
486     while( gp = boolgads )
487     {
488 	boolgads = boolgads->next;
489 
490 	if( ip = gp->gad.GadgetText )
491 	{
492 	    if( ip->IText ) free( ip->IText );
493 	    free( ip );
494 	}
495 	free( gp );
496     }
497 }
498 
499 #ifdef	TESTING
500 /*
501  * Put a 3-D motif border around the gadget.  String gadgets or those
502  * which do not have highlighting are rendered down.  Boolean gadgets
503  * are rendered in the up position by default.
504  */
505 
SetBorder(gd,val)506 void SetBorder( gd, val )
507     register struct Gadget *gd;
508     int val;
509 {
510     register struct Border *bp;
511     register short *sp;
512     register int i;
513     int borders = 6;
514 
515     /* Allocate two border structures one for up image and one for down
516      * image, plus vector arrays for the border lines.
517      */
518 
519     if( gd->GadgetType == STRGADGET )
520 	borders = 12;
521 
522     if( ( bp = malloc( ( ( sizeof( struct Border ) * 2 ) +
523 	    ( sizeof( short ) * borders ) ) * 2 ) ) == NULL )
524     {
525 	return;
526     }
527 
528     /* Remove any special rendering flags to avoid confusing intuition
529      */
530 
531     gd->Flags &= ~(GADGHIGHBITS|GADGIMAGE|GRELWIDTH|
532 		    GRELHEIGHT|GRELRIGHT|GRELBOTTOM);
533 
534     sp = (short *)(bp + 4);
535     if( val == 0 || val == 2 ||
536 	gd->GadgetType == STRGADGET || ( gd->GadgetType == BOOLGADGET &&
537 		( gd->Flags & GADGHIGHBITS ) == GADGHNONE ) )
538     {
539 	/* For a string gadget, we expand the border beyond the area where
540 	 * the text will be entered.
541 	 */
542 
543 	sp[0] = -1;
544 	sp[1] = gd->Height - 1;
545 	sp[2] = -1;
546 	sp[3] = -1;
547 	sp[4] = gd->Width + 1;
548 	sp[5] = -1;
549 
550 	sp[6] = gd->Width + 3;
551 	sp[7] = -2;
552 	sp[8] = gd->Width + 3;
553 	sp[9] = gd->Height + 1;
554 	sp[10] = -2;
555 	sp[11] = gd->Height + 1;
556 
557 	sp[12] = -2;
558 	sp[13] = gd->Height;
559 	sp[14] = -2;
560 	sp[15] = -2;
561 	sp[16] = gd->Width + 2;
562 	sp[17] = -2;
563 	sp[18] = gd->Width + 2;
564 	sp[19] = gd->Height;
565 	sp[20] = -2;
566 	sp[21] = gd->Height;
567 
568 	for( i = 0; i < 3; ++i )
569 	{
570 	    bp[ i ].LeftEdge = bp[ i ].TopEdge = -1;
571 	    if( val == 2 )
572 		bp[ i ].FrontPen = ( i == 0 || i == 1 ) ? 2 : 1;
573 	    else
574 		bp[ i ].FrontPen = ( i == 0 || i == 1 ) ? 1 : 2;
575 
576 	    /* Have to use JAM2 so that the old colors disappear. */
577 	    bp[ i ].BackPen = 0;
578 	    bp[ i ].DrawMode = JAM2;
579 	    bp[ i ].Count = ( i == 0 || i == 1 ) ? 3 : 5;
580 	    bp[ i ].XY = &sp[ i*6 ];
581 	    bp[ i ].NextBorder = ( i == 2 ) ? NULL : &bp[ i + 1 ];
582 	}
583 
584 	/* Set the up image */
585 	gd->GadgetRender = (APTR) bp;
586 
587 	/* Same image for select image */
588 	gd->SelectRender = (APTR) bp;
589 
590 	gd->LeftEdge++;
591 	gd->TopEdge++;
592 	gd->Flags |= GADGHCOMP;
593     }
594     else
595     {
596 	/* Create the border vector values for up and left side, and
597 	 * also the lower and right side.
598 	 */
599 
600 	sp[0] = 0;
601 	sp[1] = gd->Height-1;
602 	sp[2] = 0;
603 	sp[3] = 0;
604 	sp[4] = gd->Width-1;
605 	sp[5] = 0;
606 
607 	sp[6] = gd->Width-1;
608 	sp[7] = 0;
609 	sp[8] = gd->Width-1;
610 	sp[9] = gd->Height-1;
611 	sp[10] = 0;
612 	sp[11] = gd->Height-1;
613 
614 	/* We are creating 4 sets of borders, the two sides of the
615 	 * rectangle share the border vectors with the opposite image,
616 	 * but specify different colors.
617 	 */
618 
619 	for( i = 0; i < 4; ++i )
620 	{
621 	    bp[ i ].TopEdge = bp[ i ].LeftEdge = 0;
622 
623 	    /* A GADGHNONE is always down */
624 
625 	    if( val != 3 && gd->GadgetType == BOOLGADGET &&
626 		( gd->Flags & GADGHIGHBITS ) != GADGHNONE )
627 	    {
628 		bp[ i ].FrontPen =
629 		    ( i == 1 || i == 2 ) ? 2 : 1;
630 	    }
631 	    else
632 	    {
633 		bp[ i ].FrontPen =
634 		    ( i == 1 || i == 3 ) ? 1 : 2;
635 	    }
636 
637 	    /* Have to use JAM2 so that the old colors disappear. */
638 	    bp[ i ].BackPen = 0;
639 	    bp[ i ].DrawMode = JAM2;
640 	    bp[ i ].Count = 3;
641 	    bp[ i ].XY = &sp[ 6 * ((i &1) != 0) ];
642 	    bp[ i ].NextBorder =
643 		( i == 1 || i == 3 ) ? NULL : &bp[ i + 1 ];
644 	}
645 
646 	/* bp[0] and bp[1] two pieces for the up image */
647 	gd->GadgetRender = (APTR) bp;
648 
649 	/* bp[2] and bp[3] two pieces for the down image */
650 	gd->SelectRender = (APTR) (bp + 2);
651 	gd->Flags |= GADGHIMAGE;
652     }
653 }
654 #endif
655