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