1 /*
2  *   Copyright (C) 1989-1992 Yale University
3  *
4  *   This work is distributed in the hope that it will be useful; you can
5  *   redistribute it and/or modify it under the terms of the
6  *   GNU General Public License as published by the Free Software Foundation;
7  *   either version 2 of the License,
8  *   or any later version, on the following conditions:
9  *
10  *   (a) YALE MAKES NO, AND EXPRESSLY DISCLAIMS
11  *   ALL, REPRESENTATIONS OR WARRANTIES THAT THE MANUFACTURE, USE, PRACTICE,
12  *   SALE OR
13  *   OTHER DISPOSAL OF THE SOFTWARE DOES NOT OR WILL NOT INFRINGE UPON ANY
14  *   PATENT OR
15  *   OTHER RIGHTS NOT VESTED IN YALE.
16  *
17  *   (b) YALE MAKES NO, AND EXPRESSLY DISCLAIMS ALL, REPRESENTATIONS AND
18  *   WARRANTIES
19  *   WHATSOEVER WITH RESPECT TO THE SOFTWARE, EITHER EXPRESS OR IMPLIED,
20  *   INCLUDING,
21  *   BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A
22  *   PARTICULAR
23  *   PURPOSE.
24  *
25  *   (c) LICENSEE SHALL MAKE NO STATEMENTS, REPRESENTATION OR WARRANTIES
26  *   WHATSOEVER TO
27  *   ANY THIRD PARTIES THAT ARE INCONSISTENT WITH THE DISCLAIMERS BY YALE IN
28  *   ARTICLE
29  *   (a) AND (b) above.
30  *
31  *   (d) IN NO EVENT SHALL YALE, OR ITS TRUSTEES, DIRECTORS, OFFICERS,
32  *   EMPLOYEES AND
33  *   AFFILIATES BE LIABLE FOR DAMAGES OF ANY KIND, INCLUDING ECONOMIC DAMAGE OR
34  *   INJURY TO PROPERTY AND LOST PROFITS, REGARDLESS OF WHETHER YALE SHALL BE
35  *   ADVISED, SHALL HAVE OTHER REASON TO KNOW, OR IN FACT SHALL KNOW OF THE
36  *   POSSIBILITY OF THE FOREGOING.
37  *
38  */
39 
40 /* -----------------------------------------------------------------
41 FILE:	    dialog.c
42 DESCRIPTION:creates a dialog box for TimberWolf pgms.
43     It returns information for the user.
44 CONTENTS:   TWDRETURNPTR TWdialog( fieldp )
45 		TWDIALOGPTR fieldp ;
46 	    TWDRETURNPTR TWread_dialog( filename )
47 		char *filename ;
48 DATE:	    Aug 16, 1989 - original coding.
49 REVISIONS:  Sep 16, 1989 - all debug directed to stderr.
50 	    Sep 23, 1989 - rewrote data structures to handle cases
51 	    Oct  2, 1989 - now user can set position of dialog box.
52 		Also fixed bug with font size.
53 	    Feb 20, 1990 - tell the user data has changed.
54 	    Sep 25, 1990 - added B&W mode.
55 	    Dec  5, 1990 - now handle stipple correctly and added
56 		user_function to the dialog procedure.
57 	    Dec 27, 1990 - eliminated multiple redraws upon startup.
58 	    Mon Jan  7 18:17:44 CST 1991 - made SAFE_WAIT_TIME
59 		user programmable for slow machines.
60 	    Fri Jan 18 18:36:44 PST 1991 - added event debug.
61 	    Fri Jan 25 16:17:21 PST 1991 - now look for backspace
62 		or delete key when in window.
63 	    Fri Feb 22 23:36:13 EST 1991 - wait for screen to appear.
64 	    Thu Mar  7 01:24:55 EST 1991 - added focus requests for
65 		SUN openwindows.  Also added draw screen refresh.
66 	    Sun Nov  3 12:50:53 EST 1991 - fixed gcc complaints.
67 	    Thu Jan 30 02:55:16 EST 1992 - added window manager hints
68 		and now allow different font for dialog box.
69 ----------------------------------------------------------------- */
70 
71 #ifndef NOGRAPHICS
72 
73 #include <stdio.h>
74 #include <string.h>
75 #include <X11/Xlib.h>
76 #include <X11/Xatom.h>
77 #include <X11/Xutil.h>
78 #include <X11/cursorfont.h>
79 
80 /* #define  TURNOFFPRINTD */
81 
82 #include <yalecad/base.h>
83 #include <yalecad/file.h>
84 #include <yalecad/colors.h>
85 #include <yalecad/message.h>
86 #include <yalecad/hash.h>
87 #include <yalecad/string.h>
88 #include <yalecad/debug.h>
89 #include <yalecad/draw.h>
90 #include <yalecad/dialog.h>
91 #include <yalecad/time.h>
92 #include "info.h"
93 
94 #define WHITE       1                /* white parent gc is one in array */
95 #define BLACK       2                /* black parent gc is two in array */
96 #define CANCEL      0                /* code for cancel menu activity */
97 #define DELETE_CH   '\177'           /* delete character */
98 #define BACKSPACE   '\010'           /* backspace character */
99 #define RETURN      '\015'           /* return charachter */
100 #define COMMENT     '#'              /* comment character in first col */
101 #define RATIO       0.70
102 #define ACCEPTWIN   0
103 #define REJECTWIN   1
104 
105 /* definitions for parsing dialog file */
106 #define FIELD      0
107 #define FIELDNUM   1
108 #define COLUMN     2
109 #define ROW        3
110 #define STRING     4
111 #define LEN        5
112 #define TYPE       6
113 #define COLOR      7
114 #define CASEGROUP  8
115 
116 /* definitions for the fonts */
117 #define NEW_FONT    TRUE
118 #define REVERT_FONT 2
119 
120 static TWINFOPTR    infoS ;          /* information about main details */
121 static Display      *dpyS;           /* the display */
122 static Window       menuS;           /* the current menu window */
123 static Window       wS;              /* the main TW display window */
124 static Window       dialogS;         /* the dialog display window */
125 static GC           *contextArrayS ; /* array of context window */
126 static GC           reverseGCS ;     /* reverse gc for dialog  */
127 static INT          screenS ;        /* the current screen */
128 static UNSIGNED_INT backgrdS ;
129 static UNSIGNED_INT foregrdS ;
130 static INT          winwidthS ;      /* window width */
131 static Window       *winS ;          /* contains info about menus */
132 static XFontStruct  *fontinfoS ;     /* font information */
133 static Font         fontS ;          /* current font */
134 static int          xdS ;            /* origin of dialog window */
135 static int          ydS ;            /* origin of dialog window */
136 static INT          fwidthS ;        /* font width in pixels */
137 static INT          fheightS ;       /* font height in pixels */
138 static INT          numwinS ;        /* number of window in dialog box */
139 static TWDRETURNPTR dataS ;          /* return data array */
140 static TWDIALOGPTR  fieldS ;         /* the current dialog array */
141 
142 /* function definitions */
143 static INT world2pix_x(int) ;
144 static INT world2pix_y(int) ;
145 static INT world2fonty(int) ;
146 static INT pixlen() ;
147 static void set_stipple_font( P2(BOOL stippleOn, INT font_change ) ) ;
148 static void debug_dialog( P1( TWDIALOGPTR fieldp ) ) ;
149 static void check_cases( P3( TWDIALOGPTR fieldp, INT select,
150     			INT (*user_function)() )) ;
151 static void draw_fields( P1(TWDIALOGPTR fieldp) ) ;
152 static void TWfreeWindows() ;
153 static void find_font_boundary() ;
154 static void edit_field( P4( INT field, Window win, XEvent event, INT (*user_function)() ) ) ;
155 
156 /* build a dialog box and get info */
TWdialog(fieldp,dialogname,user_function)157 TWDRETURNPTR TWdialog( fieldp, dialogname, user_function )
158 TWDIALOGPTR fieldp ;
159 char *dialogname ;
160 INT (*user_function)() ;
161 {
162     UNSIGNED_INT white, black ;
163     INT i ;               /* counter */
164     long event_mask ;     /* set up event catching with this mask */
165     INT screenwidth ;     /* width of root window */
166     unsigned int widthd ; /* adjusted width of dialog screen */
167     INT screenheight ;    /* height of root window */
168     unsigned int heightd ;/* adjusted height of dialog screen */
169     INT win_num ;         /* window counter */
170     INT time ;            /* current time */
171     TWDIALOGPTR fptr ;    /* current dialog field */
172     TWDRETURNPTR dptr ;   /* current return field */
173     BOOL press ;          /* tells whether button has been pushed */
174     BOOL bw ;             /* tells whether display is color or not */
175     BOOL foundWindow ;    /* used in window search to find match */
176     static INT lasttimeL; /* last time of exposure event */
177     XEvent event ;        /* describes button event */
178     Window win ;          /* temporary for selected window */
179     Cursor cursor ;       /* cursor for typing */
180     char *winstr ;        /* used for get user window default */
181     INT  m ;              /* mask for determining window position */
182     char resource[LRECL] ;/* look for match in database */
183     XSizeHints hints ;	  /* setup hints for window manager */
184     char *font ;          /* user font request */
185     BOOL font_loaded ;    /* whether dialog box has its own font */
186 
187 
188     /* get static information from main draw module */
189     /* try to do crude object oriented programming */
190     infoS = TWgetDrawInfo() ;
191     /* save display info for future use */
192     dpyS = infoS->dpy ;
193     screenS = infoS->screen ;
194     wS = infoS->rootWindow ;
195 
196     black = BlackPixel(dpyS,screenS);
197     white = WhitePixel(dpyS,screenS);
198     backgrdS = black ;
199     foregrdS = white ;
200 
201     /* check whether machine is color or not */
202     if( (bw = XDisplayCells( dpyS, screenS )) > 2 ){
203 	/* if color number of display cells > 0 */
204 	bw = FALSE ;
205     } else {
206 	bw = TRUE ;
207     }
208 
209     /* calculate where to put master window */
210     /* we want to center the window and take 70% of */
211     /* the available screen */
212     screenwidth = XDisplayWidth(dpyS,screenS);
213     screenheight = XDisplayHeight(dpyS,screenS);
214 
215     sprintf( resource, "geometry_%s", dialogname ) ;
216     D( "dialog", fprintf( stderr, "resource:%s\n", resource ) ) ;
217     if( winstr = XGetDefault( dpyS, GRAPHICS, resource )){
218 	m = XParseGeometry( winstr,&xdS,&ydS,&widthd,&heightd) ;
219 	if( m & XNegative ){
220 	    xdS += screenwidth ;
221 	}
222 	if( m & YNegative ){
223 	    ydS += screenheight ;
224 	}
225 	/* these two lines insure that uses doesn't have to press */
226 	/* button using twm window manager */
227 	if( xdS == 0 ) xdS++ ;
228 	if( ydS == 0 ) ydS++ ;
229 	hints.flags = USPosition | USSize ;
230     } else {
231 	widthd  = (INT) (RATIO * (DOUBLE) screenwidth ) ;
232 	heightd = (INT) (RATIO * (DOUBLE) screenheight ) ;
233 	xdS = (screenwidth - widthd ) / 2 ;
234 	ydS = (screenheight - heightd ) / 2 ;
235 	hints.flags = PPosition | PSize ;
236     }
237     sprintf( resource, "font_%s", dialogname ) ;
238     D( "dialog", fprintf( stderr, "font resource:%s\n", resource ) ) ;
239     /* set font and get font info */
240     font_loaded = FALSE ;
241     if(font = XGetDefault( dpyS, GRAPHICS, resource )){
242 	if( strcmp( font, infoS->fontname ) != STRINGEQ ){
243 	    fontinfoS = TWgetfont( font, &fontS ) ;
244 	    font_loaded = TRUE ;
245 	}
246     } else {
247 	fontinfoS = infoS->fontinfo ;
248 	fontS = fontinfoS->fid ;
249     }
250 
251     /* normal case - need to create menu window */
252     dialogS = XCreateSimpleWindow( dpyS, wS, xdS, ydS,
253 	widthd, heightd, 1L, backgrdS, foregrdS ) ;
254 
255     event_mask = ExposureMask | VisibilityChangeMask ;
256     XSelectInput(dpyS,dialogS,event_mask);
257 
258     cursor = XCreateFontCursor( dpyS, XC_hand2 ) ;
259     XDefineCursor( dpyS, dialogS, cursor ) ;
260 
261     /* set the window manager hints */
262     hints.x = xdS ;
263     hints.y = ydS ;
264     hints.width = widthd ;
265     hints.height = heightd ;
266     XSetStandardProperties(dpyS,dialogS,dialogname,dialogname,None,NULL,0,&hints);
267 
268     /* set graphic contexts */
269     contextArrayS = infoS->graphicContext ;
270     reverseGCS = contextArrayS[WHITE] ;
271     set_stipple_font( FALSE, font_loaded ) ;
272 
273     /* count number of windows required */
274     fieldS = fieldp ;
275     numwinS = 0 ;
276     for( i=0 ; fieldp[i].row ; i++ ){
277 	numwinS++ ;
278     }
279     /* allocate an array of windows */
280     winS = YCALLOC( numwinS+1, Window ) ;
281     dataS = YCALLOC( numwinS+1,TWDRETURNBOX ) ;
282 
283     /* find pixel bounding box */
284     /* we assume fixed fonts but variable fonts will be ok */
285     find_font_boundary() ;
286 
287     for( i=0 ; i < numwinS ; i++ ){
288 	fptr = &(fieldp[i]) ;
289 	dptr = &(dataS[i]) ;
290 	if( fptr->string ){
291 	    dptr->string = Ystrclone( fptr->string ) ;
292 	} else {
293 	    dptr->string = NULL ;
294 	}
295 	if( bw ){
296 	    /* on a black and white machine set color to backgrd color */
297 	    fptr->color = BLACK ;
298 	}
299 	if( fptr->type == INPUTTYPE || fptr->type == BUTTONTYPE ){
300 	    winS[i] = XCreateSimpleWindow( dpyS, dialogS,
301 		world2pix_x( fptr->column ),
302 		world2pix_y( fptr->row - 1 ),
303 		pixlen( fptr->len ), fheightS,
304 		1L, backgrdS, foregrdS ) ;
305 	    XDefineCursor( dpyS, winS[i], cursor ) ;
306 
307 	} else if( fptr->type == CASETYPE ){
308 	    /* initial on button of a case switch */
309 	    ASSERTNCONT( fptr->group > 0 && fptr->group < numwinS,
310 		"TWdialog", "init_switch out of bounds\n" ) ;
311 	    dataS[fptr->group].bool = TRUE ;
312 	}
313     }
314     /* now raise all the subwindows */
315     /* and initialize input selection*/
316     for( i=0; i< numwinS; i++ ){
317 	if(!(winS[i])){
318 	    /* skip over label fields which don't have windows */
319 	    continue ;
320 	}
321 
322 	/* now set all window masks */
323 	fptr = &(fieldp[i]) ;
324 	if( fptr->type == BUTTONTYPE ){
325 	    event_mask = ButtonPressMask | EnterWindowMask | LeaveWindowMask ;
326 	} else if( fptr->type == INPUTTYPE ){
327 	    event_mask = KeyPressMask | EnterWindowMask | LeaveWindowMask ;
328 	} else {
329 	    /* use just exposure mask here */
330 	    event_mask = ExposureMask ;
331 	}
332 	XSelectInput(dpyS,winS[i],event_mask);
333 	XMapRaised( dpyS, winS[i] ) ;
334     }
335 
336     /* now raise menu window so we can see it */
337     XMapWindow( dpyS, dialogS ) ;
338 
339     /* wait for all the windows to be raised */
340     XSync( dpyS, FALSE ) ;
341     /* -------------------------------------------------------------
342        Now wait to window to become visible.  This code is necessary
343        since some window managers (uwm) map the window as a ghost
344        image and wait for user to resize window.  Other window
345        managers (twm) map window as requested.  Need to accomodate
346        both.
347     -------------------------------------------------------------- */
348     while( TRUE ){
349 	if( XCheckTypedWindowEvent(dpyS,dialogS,VisibilityNotify,&event)){
350 	    if( event.xvisibility.state == VisibilityUnobscured ||
351 		event.xvisibility.state == VisibilityPartiallyObscured ){
352 		break ;
353 	    }
354 	}
355     }
356     event_mask = ExposureMask ;
357     XSelectInput(dpyS,dialogS,event_mask);
358     XClearWindow( dpyS, dialogS ) ;
359 
360     draw_fields( fieldp ) ;
361     /* update time for slow machines */
362     (void) YcurTime( &lasttimeL ) ;
363 
364     /* now look for button press to end */
365     event_mask = ButtonPressMask | KeyPressMask | EnterWindowMask |
366 	LeaveWindowMask | ExposureMask ;
367     while( TRUE ){
368 	if( XCheckMaskEvent( dpyS, event_mask, &event ) ){
369 	    D( "TWdialog/event",
370 		fprintf( stderr, "Event:%d window:%d\n",
371 		    event.type, event.xany.window ) ;
372 	    ) ;
373 	    switch( event.type ){
374 	    case ButtonPress:
375 		/* how we exit */
376 		win = event.xbutton.window ;
377 		if( win == winS[ACCEPTWIN] ){
378 		    TWfreeWindows() ;
379 		    set_stipple_font( TRUE, REVERT_FONT ) ;
380 		    return( dataS ) ;
381 		} else if( win == winS[REJECTWIN] ){
382 		    TWfreeWindows() ;
383 		    set_stipple_font( TRUE, REVERT_FONT ) ;
384 		    return( NULL ) ;
385 		} else {
386 		    /* BUTTON TYPE which is a case switch */
387 		    foundWindow = FALSE ;
388 		    for( i = 0; i < numwinS; i++ ){
389 			if( win == winS[i] ){
390 			    foundWindow = TRUE ;
391 			    break ;
392 			}
393 		    } /* end search for window */
394 		    if( foundWindow ){
395 			check_cases( fieldp, i, user_function ) ;
396 		    }
397 		}
398 		break ;
399 	    case EnterNotify:
400 		/* light up window */
401 		/* find window match */
402 		win = event.xcrossing.window ;
403 		foundWindow = FALSE ;
404 		for( i = 0; i < numwinS; i++ ){
405 		    if( win == winS[i] ){
406 			foundWindow = TRUE ;
407 			break ;
408 		    }
409 		} /* end search for window */
410 
411 		if( foundWindow ){ /* a match light up window */
412 		    fptr = &(fieldp[i]) ;
413 		    dptr = &(dataS[i]) ;
414 		    XFillRectangle( dpyS,win, contextArrayS[fptr->color],
415 			0,0,
416 			pixlen( fptr->len ), fheightS ) ;
417 		    if( dptr->string ){
418 			XDrawString( dpyS, win, reverseGCS,
419 			    0L, world2fonty( 0L ),
420 			    dptr->string, strlen(dptr->string)) ;
421 		    }
422 		    XSetInputFocus( dpyS, win,
423 			RevertToPointerRoot, CurrentTime ) ;
424 		}
425 		break ;
426 
427 	    case LeaveNotify:
428 		/* turn off window */
429 		/* find window match */
430 		win = event.xcrossing.window ;
431 		foundWindow = FALSE ;
432 		for( i = 0; i < numwinS; i++ ){
433 		    if( win == winS[i] ){
434 			foundWindow = TRUE ;
435 			break ;
436 		    }
437 		} /* end search for window */
438 
439 		if( foundWindow ){ /* turn off window */
440 		    dptr = &(dataS[i]) ;
441 		    XClearWindow( dpyS, win ) ;
442 		    if( dptr->string ){
443 			if( fieldp[i].type != BUTTONTYPE ){
444 			    /* normal case */
445 			    XDrawString( dpyS, winS[i],
446 				contextArrayS[fptr->color],
447 				0L, world2fonty( 0L ),
448 				dptr->string,
449 				strlen(dptr->string)) ;
450 			} else if( dptr->bool ){
451 			    /* a case switch that is on */
452 			    XFillRectangle( dpyS,win,
453 				contextArrayS[fptr->color],
454 				0,0,
455 				pixlen( fptr->len ), fheightS ) ;
456 			    XDrawString( dpyS, win, reverseGCS,
457 				0L, world2fonty( 0L ),
458 				dptr->string, strlen(dptr->string)) ;
459 			} else {
460 			    /* a case switch that is off */
461 			    XDrawString( dpyS, winS[i],
462 				contextArrayS[fptr->color],
463 				0L, world2fonty( 0L ),
464 				dptr->string,
465 				strlen(dptr->string)) ;
466 			}
467 		    }
468 		    XSetInputFocus( dpyS, PointerRoot,
469 			RevertToParent, CurrentTime );
470 		}
471 		break ;
472 	    case KeyPress:
473 		win = event.xkey.window ;
474 		foundWindow = FALSE ;
475 		for( i = 0; i < numwinS; i++ ){
476 		    if( win == winS[i] ){
477 			foundWindow = TRUE ;
478 			break ;
479 		    }
480 		} /* end search for window */
481 
482 		if( foundWindow ){ /* turn off window */
483 		    edit_field( i, win, event, user_function ) ;
484 		}
485 		break ;
486 	    case Expose:
487 		win = event.xexpose.window ;
488 		if( win == infoS->drawWindow ){
489 		    (*infoS->refresh_func)() ;
490 		} else {
491 		    /* window managers sometimes send us too many */
492 		    /* exposure events therefore check time and */
493 		    /* make multiple exposures invalid */
494 		    if( event.xexpose.count == 0 ){
495 			(void) YcurTime( &time ) ;
496 			if( time - lasttimeL < TWsafe_wait_timeG ){
497 			    D( "TWdialog/exposure",
498 				fprintf( stderr,
499 				    "Dialog Exposure:0 @time = %d\n",
500 				    time);
501 			    ) ;
502 			    break ;
503 			}
504 			D( "TWdialog/exposure",
505 			    fprintf( stderr,
506 				"Dialog Exposure:1 @time = %d\n",
507 				time);
508 			) ;
509 			draw_fields( fieldp ) ;
510 			/* update time for slow machines */
511 			(void) YcurTime( &time ) ;
512 			lasttimeL = time ;
513 		    }
514 		}
515 		break ;
516 	    } /* end event switch */
517 	} /* end check on event */
518     } /* end wait loop */
519 
520 } /* end TWdialog */
521 
set_stipple_font(BOOL stippleOn,INT font_change)522 static void set_stipple_font( BOOL stippleOn, INT font_change )
523 {
524     INT i ;        /* counter */
525 
526     if( infoS->stipple ){
527 	if( stippleOn ){
528 	    for( i = 3; i <= infoS->numColors; i++ ){
529 		XSetFillStyle( dpyS, contextArrayS[i], FillTiled ) ;
530 	    }
531 	} else {
532 	    for( i = 3; i <= infoS->numColors; i++ ){
533 		XSetFillStyle( dpyS, contextArrayS[i], FillSolid ) ;
534 	    }
535 	}
536     }
537     if( font_change ){
538 	if( font_change == NEW_FONT ){
539 	    for( i=0; i <= infoS->numColors; i++ ){
540 		XSetFont( dpyS, contextArrayS[i], fontS ) ;
541 	    }
542 	} else if( font_change = REVERT_FONT ){
543 	    for( i=0; i <= infoS->numColors; i++ ){
544 		XSetFont( dpyS, contextArrayS[i], infoS->fontinfo->fid ) ;
545 	    }
546 	}
547     }
548 } /* end set_stipple_font */
549 
550 /* check the case fields and set all member of group to false */
check_cases(fieldp,select,user_function)551 static void check_cases( fieldp, select, user_function )
552 TWDIALOGPTR fieldp ;
553 INT select ;
554 INT (*user_function)() ;
555 {
556     INT i ;               /* counter */
557     INT group ;           /* case group */
558     TWDIALOGPTR fptr ;    /* current dialog field */
559     TWDRETURNPTR dptr ;   /* current return field */
560 
561     group = fieldp[select].group ;
562     for( i=0 ; i < numwinS ; i++ ){
563 	fptr = &(fieldp[i]) ;
564 	dptr = &(dataS[i]) ;
565 	if( fptr->group == group ){
566 	    if( i == select ){
567 		dptr->bool = TRUE ;
568 	    } else {
569 		dptr->bool = FALSE ;
570 	    }
571 	}
572     }
573     if( user_function ){
574 	(*user_function)( dataS, select ) ;
575     }
576     /* redraw fields so user can see change */
577     draw_fields( fieldp ) ;
578 
579     XFlush( dpyS ) ;
580 
581 } /* end check_cases */
582 
draw_fields(TWDIALOGPTR fieldp)583 static void draw_fields( TWDIALOGPTR fieldp )
584 {
585     INT i ;               /* counter */
586     TWDIALOGPTR fptr ;    /* current dialog field */
587     TWDRETURNPTR dptr ;   /* current return field */
588 
589     /* now draw all the strings to dialog window */
590     XClearWindow( dpyS, dialogS ) ;
591     for( i=0 ; i < numwinS ; i++ ){
592 	fptr = &(fieldp[i]) ;
593 	dptr = &(dataS[i]) ;
594 	if(!(dptr->string)){
595 	    /* avoid null strings */
596 	    continue ;
597 	}
598 	if( fptr->type == LABELTYPE || fptr->type == CASETYPE ){
599 	    XDrawString( dpyS, dialogS, contextArrayS[fptr->color],
600 		world2pix_x( fptr->column ),
601 		world2fonty( fptr->row - 1 ),
602 		dptr->string, strlen(dptr->string)) ;
603 	} else if( fptr->type == INPUTTYPE ){
604 	    /* input case */
605 	    XClearWindow( dpyS, winS[i] ) ;
606 	    XDrawString( dpyS, winS[i], contextArrayS[fptr->color],
607 		0L, world2fonty( 0L ),
608 		dptr->string, strlen(dptr->string)) ;
609 	} else if( fptr->type == BUTTONTYPE ){
610 	    XClearWindow( dpyS, winS[i] ) ;
611 	    if( dptr->bool ){
612 		/* true initially */
613 		XFillRectangle( dpyS,winS[i], contextArrayS[fptr->color],
614 		    0,0,
615 		    pixlen( fptr->len ), fheightS ) ;
616 		XDrawString( dpyS, winS[i], reverseGCS,
617 		    0L, world2fonty( 0L ),
618 		    dptr->string, strlen(dptr->string)) ;
619 	    } else { /* off */
620 		XDrawString( dpyS, winS[i], contextArrayS[fptr->color],
621 		    0L, world2fonty( 0L ),
622 		    dptr->string, strlen(dptr->string)) ;
623 	    }
624 	}
625     }
626 } /* end draw_fields */
627 
628 
TWfreeWindows()629 static void TWfreeWindows()
630 {
631     INT i, j ;              /* counters */
632 
633     for( i = 0; winS[i] ; i++ ){
634 	/* we must free window this way */
635 	XDestroyWindow( dpyS, winS[i] ) ;
636     }
637     YFREE( winS ) ;
638     XDestroyWindow( dpyS, dialogS ) ;
639 
640 } /* end TWfreeWindows */
641 
find_font_boundary()642 static void find_font_boundary()
643 {
644     fwidthS = fontinfoS->max_bounds.rbearing -
645 	fontinfoS->min_bounds.lbearing ;
646 
647     fheightS = fontinfoS->ascent + fontinfoS->descent ;
648 
649 } /* end find_font_boundary */
650 
651 /* tranforms the world coordinate character column format */
652 /* to pixel coordinates */
world2pix_x(int x)653 static INT world2pix_x( int x )
654 {
655     return( x * fwidthS ) ;
656 } /* end world2pix_x */
657 
world2pix_y(int y)658 static INT world2pix_y( int y )
659 {
660     return( y * fheightS ) ;
661 } /* end world2pix_y */
662 
world2fonty(int y)663 static INT world2fonty( int y )
664 {
665     return( (++y) * fheightS - fontinfoS->max_bounds.descent ) ;
666 } /* end world2pix_y */
667 
668 /* change length of string to pixel length */
pixlen(length)669 static INT pixlen( length )
670 INT length ;
671 {
672     return( fwidthS * length ) ;
673 } /* end pixlen */
674 
edit_field(INT field,Window win,XEvent event,INT (* user_function)())675 static void edit_field( INT field, Window win, XEvent event, INT (*user_function)() )
676 {
677     TWDIALOGPTR fptr;    /* current field of dialog */
678     TWDRETURNPTR dptr;   /* return field of dialog */
679     BOOL press ;            /* tells whether keyboard has been pushed */
680     BOOL finish ;           /* tells whether we have received a return */
681     long event_mask ;       /* setup menus */
682     char buffer[LRECL] ;    /* used for keyboard translation */
683     char curMsg[LRECL] ;    /* current value of message window */
684     char data[LRECL];       /* current value of users input */
685     KeySym keysym ;         /* return of keysym from user */
686     XComposeStatus status ; /* where a compose key was pressed */
687     INT strwidth ;          /* width of string in pixels */
688     INT dataCount ;         /* number of characters in user input */
689 
690     fptr = &( fieldS[field] ) ;
691     dptr = &( dataS[field] ) ;
692 
693     if( dptr->string ){
694 	/* initialize string buffers */
695 	dataCount = strlen( dptr->string ) ;
696 	strcpy( data, dptr->string ) ;
697 	/* now move pointer to end of current data */
698 	strwidth = XTextWidth( fontinfoS, dptr->string, dataCount) ;
699 	/* now warp pointer to message window */
700 	XWarpPointer( dpyS, None, win, 0, 0, 0, 0,
701 	    strwidth + fwidthS/3, fheightS/2 ) ;
702     } else {
703 	/* initialize string buffers */
704 	data[0] = EOS ;
705 	dataCount = 0 ;
706     }
707 
708     /* now look for keyboard action */
709     finish = FALSE ;
710     press = TRUE ; /* initially we got a keyboard event */
711     /* tell user data has changed */
712     dptr->bool = TRUE ;
713     do {
714 	if( press ){
715 	    /* initialize buffer */
716 	    buffer[0] = EOS ;
717 	    /* find what the user entered */
718 	    XLookupString( &(event.xkey), buffer,LRECL,&keysym, &status );
719 	    buffer[1] = EOS ; /* only get one character at a time */
720 	    D( "Yedit_field",fprintf( stderr, "char:%c\n", buffer[0] ) ) ;
721 
722 	    /* look to see if we got a return */
723 	    if( buffer[0] == RETURN ){
724 		finish = TRUE ;
725 		if( dptr->string ){
726 		    YFREE( dptr->string ) ;
727 		}
728 		/* return answer in dptr field */
729 		dptr->string = Ystrclone( data ) ;
730 	    } else {
731 		/* look for more data */
732 		/* but first copy the data we have */
733 		if( buffer[0] == BACKSPACE || buffer[0] == DELETE_CH ){
734 		    /* look for backspace or delete  */
735 		    if( dataCount > 0 ){
736 			dataCount-- ;
737 		    }
738 		    data[dataCount] = EOS ;
739 
740 		} else {
741 		    /* save data */
742 		    strcat( data, buffer ) ;
743 		    dataCount += strlen( buffer ) ;
744 		}
745 		/* now echo to screen */
746 		XFillRectangle( dpyS,win, contextArrayS[fptr->color],
747 		    0,0,
748 		    pixlen( fptr->len ), fheightS ) ;
749 		XDrawString( dpyS, win, reverseGCS,
750 		    0L, world2fonty( 0L ),
751 		    data, dataCount) ;
752 		D( "Yedit_field",fprintf( stderr, "data:%s\n", data ) ) ;
753 		D( "Yedit_field",fprintf( stderr,"datacount:%d\n",
754 		    dataCount ) ) ;
755 		/* now move pointer to end of current data */
756 		strwidth = XTextWidth( fontinfoS, data,dataCount) ;
757 		/* now warp pointer to message window */
758 		XWarpPointer( dpyS, None, win, 0, 0, 0, 0,
759 		    strwidth + fwidthS/3, fheightS/2 ) ;
760 
761 	    }
762 	}
763 	/* get next keyboard stroke */
764 	press = XCheckTypedWindowEvent( dpyS,win,KeyPress,&event );
765 
766     } while(!finish) ; /* end wait loop */
767 
768     if( user_function ){
769 	(*user_function)( dataS, field ) ;
770 	/* redraw fields so user can see change */
771 	draw_fields( fieldS ) ;
772     }
773 
774 
775 } /* end edit_field */
776 
777 #ifdef DEBUG
778 
TWread_dialog(filename)779 TWDIALOGPTR TWread_dialog( filename )
780 char *filename ;
781 {
782 
783 #define KEYWORD       "numfields"
784 #define FIELDKEYWORD  "field"
785 
786     FILE *fp ;
787     char buffer[LRECL], *bufferptr ;
788     char group[LRECL] ;  /* for parsing case groups */
789     char **tokens ;      /* for parsing menu file */
790     char **colors ;      /* the standard color array */
791     INT  numtokens ;     /* number of tokens on line */
792     INT  group_num ;     /* number of case fields given */
793     INT  i ;             /* counter */
794     INT  line ;          /* line number of TWmenu file */
795     INT  length ;        /* length of string */
796     INT  numfields ;     /* number of dialog fields */
797     INT  curfield ;      /* current dialog field */
798     INT  case_label ;    /* current case field */
799     INT  numcolors ;     /* the number of colors in color array */
800     BOOL found ;         /* whether color was found */
801     TWDIALOGPTR fields;  /* dialog array information */
802     TWDIALOGPTR fptr;    /* current field of dialog */
803 
804     /* get colors for dialog window */
805     infoS = TWgetDrawInfo() ;
806     colors = infoS->colors ;
807     numcolors = infoS->numColors ;
808 
809     /* parse dialog file */
810     line = 0 ;
811     curfield = -1 ;
812     group_num = 0 ;
813     fields = NULL ;
814     fp = TWOPEN( filename, "r", ABORT ) ;
815     while( bufferptr=fgets(buffer,LRECL,fp )){
816 	/* parse file */
817 	line++ ; /* increment line number */
818 	/* skip comments */
819 	if( *bufferptr == COMMENT ){
820 	    continue ;
821 	}
822 	tokens = Ystrparser( bufferptr, ";\t\n", &numtokens );
823 
824 	if( numtokens == 0 ){
825 	    /* skip over empty lines */
826 	    continue ;
827 	} else if( strcmp( tokens[0], KEYWORD ) == STRINGEQ){
828 	    /* look at first field for menu keyword */
829 
830 	    /* there better be only two tokens on this line */
831 	    if( numtokens != 2 ){
832 		sprintf( YmsgG, "Syntax error on line:%d\n", line ) ;
833 		M(ERRMSG, "TWread_dialog", YmsgG ) ;
834 		break ;
835 	    }
836 	    numfields = atoi( tokens[1] ) ;
837 	    ASSERTNBREAK( numfields > 0,"TWread_dialog","numfields must be >0\n" ) ;
838 	    fields = YCALLOC( numfields+1, TWDIALOGBOX ) ;
839 
840 	} else if( strcmp( tokens[FIELD], FIELDKEYWORD ) == STRINGEQ){
841 	    if( ++curfield >= numfields ){
842 		M( ERRMSG,"TWread_dialog","number of fields mismatch\n" ) ;
843 		return( NULL ) ;
844 	    }
845 	    fptr = &(fields[curfield]) ;
846 	    if( numtokens != 8 && numtokens != 9 ){
847 		sprintf( YmsgG, "Problem parsing line:%d in dialog file\n",
848 		    line ) ;
849 		M( ERRMSG,"TWread_dialog", YmsgG ) ;
850 		continue ;
851 
852 	    }
853 	    fptr->column = atoi( tokens[COLUMN] ) ;
854 	    fptr->row = atoi( tokens[ROW] ) ;
855 	    fptr->len = atoi( tokens[LEN] ) ;
856 	    /* check color to make sure it is valid */
857 	    found = FALSE ;
858 	    for( i=1; i <= numcolors; i++ ){
859 		if( strcmp( tokens[COLOR], colors[i] ) == STRINGEQ ){
860 		    fptr->color = i ;
861 		    found = TRUE ;
862 		    break ;
863 		}
864 	    }
865 	    if(!(found)){
866 		sprintf( YmsgG,
867 		    "Color:%s has not been allocated\n",
868 		    tokens[COLOR] ) ;
869 		M( ERRMSG, "TWread_dialog", YmsgG ) ;
870 		return( NULL ) ;
871 
872 	    }
873 	    if( strcmp( tokens[STRING], "NULL") == STRINGEQ ){
874 		fptr->string = NULL ;
875 	    } else {
876 		fptr->string = Ystrclone( tokens[STRING] ) ;
877 	    }
878 
879 	    /* look at types */
880 	    if( strcmp( tokens[TYPE], "input") == STRINGEQ ){
881 		fptr->type = INPUTTYPE ;
882 	    } else if( strcmp( tokens[TYPE], "label") == STRINGEQ ){
883 		fptr->type = LABELTYPE ;
884 	    } else if( strcmp( tokens[TYPE], "accept") == STRINGEQ ){
885 		fptr->type = BUTTONTYPE ;
886 		if( curfield != 0 ){
887 		    M( ERRMSG, "TWread_dialog",
888 			"accept must be 1st field\n" ) ;
889 		    return( NULL ) ;
890 		}
891 	    } else if( strcmp( tokens[TYPE], "reject") == STRINGEQ ){
892 		fptr->type = BUTTONTYPE ;
893 		if( curfield != 1 ){
894 		    M( ERRMSG, "TWread_dialog",
895 			"reject must be 2nd field\n" ) ;
896 		    return( NULL ) ;
897 		}
898 	    } else if( strcmp( tokens[TYPE], "clabel") == STRINGEQ ){
899 		fptr->type = BUTTONTYPE ;
900 	    } else if( strcmp( tokens[TYPE], "case") == STRINGEQ ){
901 		fptr->type = CASETYPE ;
902 		group_num++ ;
903 		if( numtokens == 9 ){
904 		    strcpy( group, tokens[CASEGROUP] ) ;
905 		    tokens = Ystrparser( group, ",\t\n", &numtokens );
906 		    ASSERTNRETURN( numtokens>0,"TWread_dialog",
907 			"no cases found\n" ) ;
908 		    for( i = numtokens-1; i >= 0;i-- ){
909 			case_label = atoi( tokens[i] ) ;
910 			ASSERTNCONT( case_label > 0 && case_label <=numfields,
911 			    "TWdialog", "case_label out of bounds\n" ) ;
912 			fields[case_label].group = group_num ;
913 		    }
914 		    /* first one in list is default on switch */
915 		    fptr->group = case_label ;
916 		} else {
917 		    M( ERRMSG, "TWread_dialog",
918 			"wrong number of tokens for case type field\n" );
919 		    return( NULL ) ;
920 		}
921 	    } else {
922 		sprintf( YmsgG, "Problem parsing line:%d in dialog file\n",
923 		    line ) ;
924 		M( ERRMSG,"TWread_dialog", YmsgG ) ;
925 	    }
926 	}
927     } /* end parsing loop */
928 
929     TWCLOSE( fp ) ;
930 
931     if( fields[0].type != BUTTONTYPE ||
932         fields[1].type != BUTTONTYPE ){
933 	M( ERRMSG, "TWread_dialog",
934 	    "accept and reject fields not setup correctly\n" ) ;
935 	return( NULL ) ;
936     }
937 
938     if( fields ){
939 	debug_dialog( fields ) ;
940 	return( fields ) ;
941     } else {
942 	return( NULL ) ;
943     }
944 
945 } /* end TWread_dialog */
946 
debug_dialog(fieldp)947 static debug_dialog( fieldp )
948 TWDIALOGPTR fieldp ;
949 {
950     INT i ;                   /* counter */
951     INT count ;               /* number of fields */
952     FILE *fp ;                /* file pointer */
953     TWDIALOGPTR fptr ;        /* temporary pointer */
954 
955     fp = TWOPEN( "dialog.h", "w", ABORT ) ;
956 
957     count = 0 ;
958     for( i=0 ; fieldp[i].row ; i++ ){
959 	count++ ;
960     }
961 
962     fprintf( fp, "static TWDIALOGBOX dialogS[%d] = {\n", count+1 ) ;
963 
964     for( i=0 ; fieldp[i].row; i++ ){
965 	fptr = &(fieldp[i]) ;
966 	fprintf( fp, "    %d,%d,%d,",fptr->row,fptr->column,fptr->len ) ;
967 	fprintf( fp, "\"%s\",%d,%d,%d,\n", fptr->string,fptr->type,
968 	    fptr->color, fptr->group ) ;
969     }
970     fprintf( fp, "    0,0,0,0,0,0,0\n" ) ;
971     fprintf( fp, "} ;\n\n" ) ;
972     TWCLOSE(fp) ;
973 } /* end debug_dialog() */
974 
975 #endif /* DEBUG */
976 
977 #endif /* no graphics */
978