1 /******************************************************************************
2 *
3 *  NSSDC/CDF                                            Windowing widgets.
4 *
5 *  Version 1.3b, 13-Nov-97, Hughes STX.
6 *
7 *  Modification history:
8 *
9 *   V1.0  19-Nov-93, J Love     Original version.
10 *   V1.0a 22-Feb-94, J Love     Spelling lesson.
11 *   V1.0b 28-Mar-94, J Love     Solaris using GNU C compiler.
12 *   V1.1  13-Dec-94, J Love     CDF V2.5.
13 *   V1.1a 23-Jan-95, J Love     Primary/alternate "current" item movement
14 *                               keys for ItemWindow.
15 *   V1.1b  7-Mar-95, J Love     Added FieldWindow.
16 *   V1.2  11-Apr-95, J Love     POSIX/CDFexport.
17 *   V1.2a 10-May-95, J Love     More CDFexport.
18 *   V1.2b 13-Jun-95, J Love     catchrX.  Linux.
19 *   V1.2c 30-Aug-95, J Love     CDFexport-related changes.  Moved online help
20 *                               widget to this file.  FSI key definitions.
21 *   V1.2d 18-Sep-95, J Love     Macintosh event handling.
22 *   V1.3  26-Aug-96, J Love     CDF V2.6.
23 *   V1.3a 31-Mar-97, J Love     Allowed fields in a FieldWindow to be longer
24 *                               than their on-screen width.
25 *   V1.3b 13-Nov-97, J Love	Windows NT/Visual C++.
26 *   V1.4  11-Jul-05, M Liu      Added MingW port for PC.
27 *
28 ******************************************************************************/
29 
30 #include "widgets.h"
31 
32 /******************************************************************************
33 * Local macros.
34 ******************************************************************************/
35 
36 #define MAX_PCT_LEN     5
37 
38 #define DIFF(a,b)       BOO((a < b),(b - a),(a - b))
39 
40 /******************************************************************************
41 * Local function prototypes.
42 ******************************************************************************/
43 
44 static void CalcItemDirections PROTOARGs((
45   int nItems, int NiLines, int *iLineNs, int *iCols, int *iLens,
46   int *iDownTo, int *iUpTo, int *iLeftTo, int *iRightTo
47 ));
48 static void DownArrow PROTOARGs((
49   int *iLineNs, int *iDownTo, int iRowT, int iRowB, Logical iScroll,
50   int *iRowN, int *itemN
51 ));
52 static void UpArrow PROTOARGs((
53   int NiLines, int *iLineNs, int *iUpTo, int iRowT, int iRowB, Logical iScroll,
54   int *iRowN, int *itemN
55 ));
56 static void NextScreen PROTOARGs((
57   int NiRows, int NiLines, int *iLineNs, int *iDownTo, int iRowB, int iRowT,
58   Logical iScroll, int *itemN, int *iRowN
59 ));
60 static void PrevScreen PROTOARGs((
61   int NiRows, int *iLineNs, int *iUpTo, int iRowT, Logical iScroll, int *iRowN,
62   int *itemN
63 ));
64 static void UpdatePctString PROTOARGs((WINDOWid, int, int, int, int, int));
65 static void DrawSection PROTOARGs((
66   WINDOWid, int, int, int, int, char **, int
67 ));
68 static void DrawField PROTOARGs((struct FieldWindowStruct *FW));
69 static void DrawFieldsSection PROTOARGs((struct FieldWindowStruct *FW));
70 static int IWtopRowLineN PROTOARGs((struct ItemWindowStruct *IW));
71 static int FWtopRowLineN PROTOARGs((struct FieldWindowStruct *FW));
72 static void DrawEditSection PROTOARGs((struct EditWindowStruct *EW));
73 static void SetEditCursor PROTOARGs((struct EditWindowStruct *EW));
74 
75 /******************************************************************************
76 * ItemWindow.
77 *
78 *       Header section.
79 *       Items section (scrollable).
80 *       Trailer section.
81 *
82 ******************************************************************************/
83 
84 #if defined(STDARG)
ItemWindow(int opT,...)85 int ItemWindow (int opT, ...)
86 #else
87 int ItemWindow (va_alist)
88 va_dcl
89 #endif
90 {
91    int op;                      /* Operation to perform (eg. create new menu,
92 				   delete menu, etc. */
93    struct ItemWindowStruct *IW; /* Menu configuration. */
94    va_list ap;
95    /***************************************************************************
96    * Start variable-length argument list scanning.
97    ***************************************************************************/
98 #if defined(STDARG)
99    va_start (ap, opT);
100    op = opT;
101 #else
102    VA_START (ap);
103    op = va_arg (ap, int);
104 #endif
105    IW = va_arg (ap, struct ItemWindowStruct *);
106    /***************************************************************************
107    * Perform desired operation.  Some operations wait for user input (by
108    * falling through the `switch' statement).
109    ***************************************************************************/
110    switch (op) {
111      /*************************************************************************
112      * Check for new/modified menu.
113      *************************************************************************/
114      case NEWiw:
115      case UPDATEiw: {
116        int nSections;           /* Number of sections on window. */
117        int nHorizLines;         /* Number of horizontal lines needed.  At
118 				   most 2 horizontal lines will be needed
119 				   to separate the 3 sections. */
120        int horizRows[2];        /* Rows at which to draw the horizontal
121 				   lines. */
122        int horizLineN;          /* Horizontal line number. */
123        int xRow;                /* Used when determining starting rows for
124 				   the various sections. */
125        Logical highlightItem = FALSE;           /* If TRUE, hightlight the
126 						   current item. */
127        /***********************************************************************
128        * Get remaining arguments.
129        ***********************************************************************/
130        IW->itemN = va_arg (ap, int);            /* Initial item number (if any
131 						   items exist). */
132        va_end (ap);
133        /***********************************************************************
134        * Calculate positions.
135        ***********************************************************************/
136        if (op == NEWiw) {
137 	 IW->nColS = IW->nColsTotal - 2;
138 	 nSections = 0;
139 	 nHorizLines = 0;
140 	 /********************************************************************\
141 	 | Start at row 1 (row 0 is top border line).
142 	 \********************************************************************/
143 	 xRow = 1;
144 	 if (IW->NhLines > 0) {
145 	   nSections++;
146 	   IW->hRowT = xRow;
147 	   IW->hRowB = IW->hRowT + IW->NhLines - 1;
148 	   xRow += IW->NhLines + 1;
149 	 }
150 	 if (IW->NiRows > 0) {
151 	   nSections++;
152 	   IW->iRowT = xRow;
153 	   IW->iRowB = IW->iRowT + IW->NiRows - 1;
154 	   xRow += IW->NiRows + 1;
155 	   if (nSections > 1) horizRows[nHorizLines++] = IW->iRowT - 1;
156 	 }
157 	 else
158 	   return FALSE;                /* An item section must exist. */
159 	 if (IW->NtLines > 0) {
160 	   nSections++;
161 	   IW->tRowT = xRow;
162 	   IW->tRowB = IW->tRowT + IW->NtLines - 1;
163 	   xRow += IW->NtLines + 1;
164 	   if (nSections > 1) horizRows[nHorizLines++] = IW->tRowT - 1;
165 	 }
166 	 IW->nRowsTotal = xRow;
167        }
168        /***********************************************************************
169        * Turn off cursor.
170        ***********************************************************************/
171        set_cursor_mode (CURSORoff);
172        /***********************************************************************
173        * Create window?
174        ***********************************************************************/
175        begin_pasteboard_update ();
176        if (op == NEWiw) {
177 	 create_virtual_display (IW->nRowsTotal, IW->nColsTotal, &(IW->wid),
178 				 BORDER, NORMAL);
179 	 paste_virtual_display (IW->wid, IW->ULrow, IW->ULcol);
180        }
181        /***********************************************************************
182        * If specified, write label to window.  If this is an update operation,
183        * first overwrite the existing label.
184        ***********************************************************************/
185        if (op == UPDATEiw) draw_horizontal_line (IW->wid, 0, 1, IW->nColS,
186 						 NORMAL, FALSE);
187        if (IW->label != NULL) {
188 	 int len = (int) strlen(IW->label);
189 	 if (len <= IW->nColS) {
190 	   int nRemainChars = IW->nColS - len;
191 	   int nCharsBefore = nRemainChars / 2;
192 	   put_chars (IW->wid, IW->label, len, 0, nCharsBefore + 1, FALSE,
193 		      REVERSE1);
194 	 }
195        }
196        /***********************************************************************
197        * If necessary, draw horizontal lines on window.
198        ***********************************************************************/
199        if (op == NEWiw) {
200 	 for (horizLineN = 0; horizLineN < nHorizLines; horizLineN++) {
201 	    draw_horizontal_line (IW->wid, horizRows[horizLineN], 0,
202 				  IW->nColsTotal-1, NORMAL, TRUE);
203 	 }
204        }
205        /***********************************************************************
206        * If specified, write header section to window.  If an update operation,
207        * it is assumed that the number of lines has not changed.
208        ***********************************************************************/
209        if (IW->NhLines > 0) DrawSection (IW->wid, IW->hRowT, IW->hRowB, 0,
210 					 IW->NhLines, IW->hLines, IW->nColS);
211        /***********************************************************************
212        * Write items section to window and `highlight' current item.  The
213        * current item is set to zero initially for a new menu (if items exist,
214        * otherwise it is set to -1).  The item section may contain lines but
215        * no items.  If an update operation, note that the number of lines may
216        * have changed.
217        ***********************************************************************/
218        if (op == UPDATEiw) {
219 	 if (IW->iUpTo != NULL) cdf_FreeMemory (IW->iUpTo,
220 					    FatalError);
221 	 if (IW->iDownTo != NULL) cdf_FreeMemory (IW->iDownTo,
222 					      FatalError);
223 	 if (IW->iLeftTo != NULL) cdf_FreeMemory (IW->iLeftTo,
224 					      FatalError);
225 	 if (IW->iRightTo != NULL) cdf_FreeMemory (IW->iRightTo,
226 					       FatalError);
227        }
228        IW->iUpTo = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
229 					   FatalError);
230        IW->iDownTo = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
231 					     FatalError);
232        IW->iLeftTo = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
233 					     FatalError);
234        IW->iRightTo = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
235 					      FatalError);
236        if (IW->NiLines > 0) {
237 	 /*******************************************************************
238 	 * One or more lines in the item section.
239 	 *******************************************************************/
240 	 if (IW->nItems > 0) {
241 	   /*****************************************************************
242 	   * One or more items in the item section.
243 	   *****************************************************************/
244 	   CalcItemDirections (IW->nItems, IW->NiLines, IW->iLineNs,
245 			       IW->iCols, IW->iLens, IW->iDownTo, IW->iUpTo,
246 			       IW->iLeftTo, IW->iRightTo);
247 	   if (IW->NiLines > IW->NiRows)
248 	     IW->iScroll = TRUE;
249 	   else
250 	     IW->iScroll = FALSE;
251 	   /*******************************************************************
252 	   * Determine row number for current item.  If this is an UPDATEiw
253 	   * operation, try to keep the row number the same as it was (or as
254 	   * close as possible).
255 	   *******************************************************************/
256 	   if (op == NEWiw)
257 	     IW->iRowN = MinInt (IW->iRowT +
258 				 IW->iLineNs[IW->itemN], IW->iRowB);
259 	   else
260 	     if (IW->iScroll) {
261 	       int nLinesFromTop = IW->iLineNs[IW->itemN];
262 	       int nRowsFromTop = IW->iRowN - IW->iRowT;
263 	       if (nLinesFromTop < nRowsFromTop)
264 		 IW->iRowN -= nRowsFromTop - nLinesFromTop;
265 	       else {
266 		 int nLinesFromBot = (IW->NiLines-1) - IW->iLineNs[IW->itemN];
267 		 int nRowsFromBot = IW->iRowB - IW->iRowN;
268 		 if (nLinesFromBot < nRowsFromBot)
269 		   IW->iRowN += nRowsFromBot - nLinesFromBot;
270 	       }
271 	     }
272 	     else
273 	       IW->iRowN = IW->iRowT + IW->iLineNs[IW->itemN];
274 	   DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
275 			IW->NiLines, IW->iLines, IW->nColS);
276 	   highlightItem = TRUE;
277 	 }
278 	 else {
279 	   /*****************************************************************
280 	   * No items in the item section (but there are some lines).  Just
281 	   * redraw lines.
282 	   *****************************************************************/
283 	   IW->iRowN = IW->iRowT;
284 	   DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
285 			IW->NiLines, IW->iLines, IW->nColS);
286 	 }
287        }
288        else {
289 	 /*******************************************************************
290 	 * No lines in the item section.  Erase lines in case there had
291 	 * previously been lines.
292 	 *******************************************************************/
293 	 IW->iRowN = IW->iRowT;
294 	 erase_display (IW->wid, IW->iRowT, 1, IW->iRowB, IW->nColS);
295        }
296        /*********************************************************************
297        * Setup/display initial percentage indicator.
298        *********************************************************************/
299        if (IW->iPct) {
300 	 int lineNt = IWtopRowLineN (IW);
301 	 if (op == NEWiw) {
302 	   IW->iPctRowN = IW->iRowB + 1;
303 	   IW->iPctColN = IW->nColsTotal - 7;
304 	 }
305 	 UpdatePctString (IW->wid, IW->NiLines, IW->NiRows, lineNt,
306 			  IW->iPctRowN, IW->iPctColN);
307        }
308        /***********************************************************************
309        * If specified, write trailer section to window.  If an update
310        * operation, it is assumed that the number of lines has not changed.
311        ***********************************************************************/
312        if (IW->NtLines > 0) DrawSection (IW->wid, IW->tRowT, IW->tRowB, 0,
313 					 IW->NtLines, IW->tLines, IW->nColS);
314        /***********************************************************************
315        * Update screen and then highlight the current item (if there is one).
316        * The current item is highlighted after the screen is updated for those
317        * situations where the cursor could not be turned off.  Highlighting
318        * the current item last will put the (possible blinking) cursor after
319        * the current item which isn't as distracting as when the cursor is at
320        * the end of the last line of the trailer section.
321        ***********************************************************************/
322        end_pasteboard_update ();
323        if (highlightItem) change_rendition (IW->wid, IW->iRowN,
324 					    IW->iCols[IW->itemN] + 1, 1,
325 					    IW->iLens[IW->itemN], REVERSE2);
326        return TRUE;
327      }
328      /*************************************************************************
329      * Check for a beep request.
330      *************************************************************************/
331      case BEEPiw:
332        ring_bell ();
333        va_end (ap);
334        return TRUE;
335      /*************************************************************************
336      * Check if menu should be deleted.
337      *************************************************************************/
338      case DELETEiw:
339        if (IW->iUpTo != NULL) cdf_FreeMemory (IW->iUpTo, FatalError);
340        if (IW->iDownTo != NULL) cdf_FreeMemory (IW->iDownTo, FatalError);
341        if (IW->iLeftTo != NULL) cdf_FreeMemory (IW->iLeftTo, FatalError);
342        if (IW->iRightTo != NULL) cdf_FreeMemory (IW->iRightTo, FatalError);
343        delete_virtual_display (IW->wid);
344        va_end (ap);
345        return TRUE;
346      /*************************************************************************
347      * Check if menu should be erased (but remain in existence).
348      *************************************************************************/
349      case UNDISPLAYiw:
350        unpaste_virtual_display (IW->wid);
351        va_end (ap);
352        return TRUE;
353      /*************************************************************************
354      * Check if menu should be redisplayed (but don't wait for input - a call
355      * with `op = READiw' should be made next).
356      *************************************************************************/
357      case REDISPLAYiw:
358        set_cursor_mode (CURSORoff);
359        paste_virtual_display (IW->wid, IW->ULrow, IW->ULcol);
360        va_end (ap);
361        return TRUE;
362      /*************************************************************************
363      * Get next input.  The cursor is positioned and turned off first.  The
364      * cursor is positioned for those cases where the cursor cannot be turned
365      * off.  A (possibly) blinking cursor at the end of the highlighted item
366      * is less distracting than where it might be.
367      *************************************************************************/
368      case READiw:
369        for (;;) {
370 	  if (IW->nItems > 0) {
371 	    set_cursor_abs (IW->wid, IW->iRowN,
372 			    IW->iCols[IW->itemN] + IW->iLens[IW->itemN] + 1);
373 	  }
374 	  set_cursor_mode (CURSORoff);
375 	  read_input (
376 #if defined(CURSESui)
377 		      IW->wid,
378 #endif
379 			       &(IW->key), PASSTHRUri, TRUE);
380 	  /********************************************************************
381 	  * Check for an exit key.  If no exit keys were specified, any key
382 	  * causes an exit.
383 	  ********************************************************************/
384 	  if (IW->exitChars[0] == NUL) {
385 	    va_end (ap);
386 	    return TRUE;
387 	  }
388 	  else {
389 	    int charN;
390 	    for (charN = 0; IW->exitChars[charN] != NUL; charN++)
391 	       if (IW->key == IW->exitChars[charN]) {
392 		 va_end (ap);
393 		 return TRUE;
394 	       }
395 	  }
396 	  /********************************************************************
397 	  * Check for next-screen key.
398 	  ********************************************************************/
399 	  if (IW->key == IW->NSkey) {
400 	    begin_pasteboard_update ();
401 	    if (IW->nItems > 0) {
402 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
403 				1, IW->iLens[IW->itemN], NORMAL);
404 	      NextScreen (IW->NiRows, IW->NiLines, IW->iLineNs, IW->iDownTo,
405 			  IW->iRowB, IW->iRowT, IW->iScroll, &(IW->itemN),
406 			  &(IW->iRowN));
407 	      DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
408 			   IW->NiLines, IW->iLines, IW->nColS);
409 	      if (IW->iPct) UpdatePctString (IW->wid, IW->NiLines, IW->NiRows,
410 					     IWtopRowLineN(IW), IW->iPctRowN,
411 					     IW->iPctColN);
412 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
413 				1, IW->iLens[IW->itemN], REVERSE2);
414 	    }
415 	    else
416 	      ring_bell ();
417 	    end_pasteboard_update ();
418 	    continue;
419 	  }
420 	  /********************************************************************
421 	  * Check for prev-screen key.
422 	  ********************************************************************/
423 	  if (IW->key == IW->PSkey) {
424 	    begin_pasteboard_update ();
425 	    if (IW->nItems > 0) {
426 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
427 				1, IW->iLens[IW->itemN], NORMAL);
428 	      PrevScreen (IW->NiRows, IW->iLineNs, IW->iUpTo, IW->iRowT,
429 			  IW->iScroll, &(IW->iRowN), &(IW->itemN));
430 	      DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
431 			   IW->NiLines, IW->iLines, IW->nColS);
432 	      if (IW->iPct) UpdatePctString (IW->wid, IW->NiLines, IW->NiRows,
433 					     IWtopRowLineN(IW), IW->iPctRowN,
434 					     IW->iPctColN);
435 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
436 				1, IW->iLens[IW->itemN], REVERSE2);
437 	    }
438 	    else
439 	      ring_bell ();
440 	    end_pasteboard_update ();
441 	    continue;
442 	  }
443 	  /********************************************************************
444 	  * Check for down arrow key.
445 	  ********************************************************************/
446 	  if (IW->key == IW_DOWN || IW->key == IW_DOWNx) {
447 	    begin_pasteboard_update ();
448 	    if (IW->nItems > 0) {
449 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
450 				1, IW->iLens[IW->itemN], NORMAL);
451 	      DownArrow (IW->iLineNs, IW->iDownTo, IW->iRowT, IW->iRowB,
452 			 IW->iScroll, &(IW->iRowN), &(IW->itemN));
453 	      DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
454 			   IW->NiLines, IW->iLines, IW->nColS);
455 	      if (IW->iPct) UpdatePctString (IW->wid, IW->NiLines, IW->NiRows,
456 					     IWtopRowLineN(IW), IW->iPctRowN,
457 					     IW->iPctColN);
458 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
459 				1, IW->iLens[IW->itemN], REVERSE2);
460 	    }
461 	    else
462 	      ring_bell ();
463 	    end_pasteboard_update ();
464 	    continue;
465 	  }
466 	  /********************************************************************
467 	  * Check for up arrow key.
468 	  ********************************************************************/
469 	  if (IW->key == IW_UP || IW->key == IW_UPx) {
470 	    begin_pasteboard_update ();
471 	    if (IW->nItems > 0) {
472 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
473 				1, IW->iLens[IW->itemN], NORMAL);
474 	      UpArrow (IW->NiLines, IW->iLineNs, IW->iUpTo, IW->iRowT,
475 		       IW->iRowB, IW->iScroll, &(IW->iRowN), &(IW->itemN));
476 	      DrawSection (IW->wid, IW->iRowT, IW->iRowB, IWtopRowLineN(IW),
477 			   IW->NiLines, IW->iLines, IW->nColS);
478 	      if (IW->iPct) UpdatePctString (IW->wid, IW->NiLines, IW->NiRows,
479 					     IWtopRowLineN(IW), IW->iPctRowN,
480 					     IW->iPctColN);
481 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
482 				1, IW->iLens[IW->itemN], REVERSE2);
483 	    }
484 	    else
485 	      ring_bell ();
486 	    end_pasteboard_update ();
487 	    continue;
488 	  }
489 	  /********************************************************************
490 	  * Check for left arrow key.
491 	  ********************************************************************/
492 	  if (IW->key == IW_LEFT || IW->key == IW_LEFTx) {
493 	    begin_pasteboard_update ();
494 	    if (IW->nItems > 0) {
495 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
496 				1, IW->iLens[IW->itemN], NORMAL);
497 	      IW->itemN = IW->iLeftTo[IW->itemN];
498 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
499 				1, IW->iLens[IW->itemN], REVERSE2);
500 	    }
501 	    else
502 	      ring_bell ();
503 	    end_pasteboard_update ();
504 	    continue;
505 	  }
506 	  /********************************************************************
507 	  * Check for right arrow key.
508 	  ********************************************************************/
509 	  if (IW->key == IW_RIGHT || IW->key == IW_RIGHTx) {
510 	    begin_pasteboard_update ();
511 	    if (IW->nItems > 0) {
512 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
513 				1, IW->iLens[IW->itemN], NORMAL);
514 	      IW->itemN = IW->iRightTo[IW->itemN];
515 	      change_rendition (IW->wid, IW->iRowN, IW->iCols[IW->itemN] + 1,
516 				1, IW->iLens[IW->itemN], REVERSE2);
517 	    }
518 	    else
519 	      ring_bell ();
520 	    end_pasteboard_update ();
521 	    continue;
522 	  }
523 	  /********************************************************************
524 	  * Check for the `refresh' key.
525 	  ********************************************************************/
526 	  if (IW->key == IW->refreshChar) {
527 	    repaint_screen ();
528 	    continue;
529 	  }
530 	  /********************************************************************
531 	  * Illegal key, ring the bell.
532 	  ********************************************************************/
533 	  ring_bell ();
534        }
535    }
536    va_end (ap);
537    return FALSE;                                      /* Illegal operation. */
538 }
539 
540 /******************************************************************************
541 * PromptWindow.
542 ******************************************************************************/
543 
544 #if defined(STDARG)
PromptWindow(int opT,...)545 int PromptWindow (int opT, ...)
546 #else
547 int PromptWindow (va_alist)
548 va_dcl
549 #endif
550 {
551    int op;                      /* Operation to perform (eg. create new
552 				   prompt window, delete window, etc.). */
553    struct PromptWindowStruct *PW;
554 				/* Prompt window configuration. */
555    va_list ap;
556    /***************************************************************************
557    * Start variable-length argument list scanning.
558    ***************************************************************************/
559 #if defined(STDARG)
560    va_start (ap, opT);
561    op = opT;
562 #else
563    VA_START (ap);
564    op = va_arg (ap, int);
565 #endif
566    PW = va_arg (ap, struct PromptWindowStruct *);
567    /***************************************************************************
568    * Perform desired operation.  Some operations wait for user input (by
569    * falling through the `switch' statement).
570    ***************************************************************************/
571    switch (op) {
572      /*************************************************************************
573      * Check for new menu or menu to be reset.
574      *************************************************************************/
575      case NEWpw:
576      case RESETpw: {
577        /***********************************************************************
578        * Get remaining arguments.
579        ***********************************************************************/
580        PW->curChar = va_arg (ap, int);
581        PW->insertMode = va_arg (ap, Logical);
582        va_end (ap);
583        /***********************************************************************
584        * Calculate positions.
585        ***********************************************************************/
586        if (op == NEWpw) {
587 	 PW->nColS = PW->nColsTotal - 2;
588 	 PW->nRowsTotal = (PW->NhLines > 0 ? 1 + PW->NhLines : 0) +
589 			  3 +
590 			  (PW->NtLines > 0 ? PW->NtLines + 1 : 0);
591 	 PW->NvCols = PW->nColsTotal - 6;
592 	 PW->vRow = (PW->NhLines > 0 ? PW->NhLines + 2 : 1);
593 	 PW->vLcol = 3;
594 	 PW->vRcol = PW->vLcol + PW->NvCols - 1;
595 	 PW->mLcol = 1;
596 	 PW->mRcol = PW->nColsTotal - 2;
597 	 if (PW->NhLines > 0) {
598 	   PW->hRowT = 1;
599 	   PW->hRowB = PW->hRowT + PW->NhLines - 1;
600 	 }
601 	 if (PW->NtLines > 0) {
602 	   PW->tRowT = (PW->NhLines > 0 ? 1 + PW->NhLines : 0) + 3;
603 	   PW->tRowB = PW->tRowT + PW->NtLines - 1;
604 	 }
605        }
606        /***********************************************************************
607        * Create window?
608        ***********************************************************************/
609        begin_pasteboard_update ();
610        if (op == NEWpw) {
611 	 create_virtual_display (PW->nRowsTotal, PW->nColsTotal, &(PW->wid),
612 				 BORDER, NORMAL);
613 	 paste_virtual_display (PW->wid, PW->ULrow, PW->ULcol);
614        }
615        /***********************************************************************
616        * If specified, write label to window.
617        ***********************************************************************/
618        if (PW->label != NULL) {
619 	 int len = (int) strlen(PW->label);
620 	 if (len <= PW->nColS) {
621 	   int nRemainChars = PW->nColS - len;
622 	   int nCharsBefore = nRemainChars / 2;
623 	   put_chars (PW->wid, PW->label, len, 0, nCharsBefore + 1, FALSE,
624 		      REVERSE1);
625 	 }
626        }
627        /***********************************************************************
628        * Draw necessary horizontal and vertical lines.
629        ***********************************************************************/
630        if (op == NEWpw) {
631 	 if (PW->NhLines > 0) draw_horizontal_line (PW->wid, PW->vRow-1, 0,
632 						    PW->nColsTotal-1, NORMAL,
633 						    TRUE);
634 	 if (PW->NtLines > 0) draw_horizontal_line (PW->wid, PW->vRow+1, 0,
635 						    PW->nColsTotal-1, NORMAL,
636 						    TRUE);
637 	 draw_vertical_line (PW->wid, PW->vRow - 1, PW->vRow + 1,
638 			     PW->vLcol - 1, NORMAL, TRUE);
639 	 draw_vertical_line (PW->wid, PW->vRow - 1, PW->vRow + 1,
640 			     PW->vRcol + 1, NORMAL, TRUE);
641        }
642        /***********************************************************************
643        * If specified, draw header.
644        ***********************************************************************/
645        if (PW->NhLines > 0) DrawSection (PW->wid, PW->hRowT, PW->hRowB, 0,
646 					 PW->NhLines, PW->hLines, PW->nColS);
647        /***********************************************************************
648        * Draw initial value (if one exists) and place cursor.  If a RESETpw
649        * operation, first erase existing value.
650        ***********************************************************************/
651        if (op == RESETpw) {
652 	 put_chars (PW->wid, " ", 1, PW->vRow, PW->mLcol, FALSE, NORMAL);
653 	 erase_display (PW->wid, PW->vRow, PW->vLcol, PW->vRow, PW->vRcol);
654 	 put_chars (PW->wid, " ", 1, PW->vRow, PW->mRcol, FALSE, NORMAL);
655        }
656        PW->curLen = (int) strlen (PW->value);
657        if (PW->curChar < PW->NvCols) {
658 	 if (PW->curLen > PW->NvCols) {
659 	   put_chars (PW->wid, PW->value, PW->NvCols, PW->vRow, PW->vLcol,
660 		      FALSE, NORMAL);
661 	   put_chars (PW->wid, ">", 1, PW->vRow, PW->mRcol, FALSE, NORMAL);
662 	 }
663 	 else
664 	   put_chars (PW->wid, PW->value, PW->curLen, PW->vRow, PW->vLcol,
665 		      FALSE, NORMAL);
666 	 PW->curCol = PW->vLcol + PW->curChar;
667        }
668        else {
669 	 put_chars (PW->wid, "<", 1, PW->vRow, PW->mLcol, FALSE, NORMAL);
670 	 if (PW->curChar < PW->curLen) {
671 	   put_chars (PW->wid, &(PW->value[PW->curChar - PW->NvCols + 1]),
672 		      PW->NvCols, PW->vRow, PW->vLcol, FALSE, NORMAL);
673 	   if (PW->curChar != PW->curLen - 1) put_chars (PW->wid, ">", 1,
674 							 PW->vRow, PW->mRcol,
675 							 FALSE, NORMAL);
676 	 }
677 	 else
678 	   put_chars (PW->wid, &(PW->value[PW->curChar - PW->NvCols + 1]),
679 		      PW->NvCols - 1, PW->vRow, PW->vLcol, FALSE, NORMAL);
680 	 PW->curCol = PW->vRcol;
681        }
682        /***********************************************************************
683        * If specified, draw trailer.
684        ***********************************************************************/
685        if (PW->NtLines > 0) DrawSection (PW->wid, PW->tRowT, PW->tRowB, 0,
686 					 PW->NtLines, PW->tLines, PW->nColS);
687        /***********************************************************************
688        * Update screen.
689        ***********************************************************************/
690        end_pasteboard_update ();
691        /***********************************************************************
692        * Position cursor (which must occur after the pasteboard batching is
693        * ended).
694        ***********************************************************************/
695        set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
696        set_cursor_mode (CURSORon);
697        return TRUE;
698      }
699      /*************************************************************************
700      * Check for a beep request.
701      *************************************************************************/
702      case BEEPpw:
703        ring_bell ();
704        va_end (ap);
705        return TRUE;
706      /*************************************************************************
707      * Check if window should be erased (but remain in existence).
708      *************************************************************************/
709      case UNDISPLAYpw:
710        unpaste_virtual_display (PW->wid);
711        va_end (ap);
712        return TRUE;
713      /*************************************************************************
714      * Check if window should be redisplayed (don't wait for input - a call
715      * with `op = READpw' should be made next).
716      *************************************************************************/
717      case REDISPLAYpw:
718        paste_virtual_display (PW->wid, PW->ULrow, PW->ULcol);
719        set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
720        set_cursor_mode (CURSORon);
721        va_end (ap);
722        return TRUE;
723      /*************************************************************************
724      * Check if window should be deleted.
725      *************************************************************************/
726      case DELETEpw:
727        delete_virtual_display (PW->wid);
728        va_end (ap);
729        return TRUE;
730      /*************************************************************************
731      * Read keystrokes until `exit' key entered.
732      *************************************************************************/
733      case READpw: {
734        int colsToEnd;           /* Number of columns from cursor to the right-
735 				   most column of the value field (including
736 				   the cursor position). */
737        int offRight;            /* Number of characters to the right of the
738 				   last column in the value field (the right
739 				   `more' indicator should be ON).  This can
740 				   be zero or negative. */
741        int offLeft;             /* Number of characters to the left of the
742 				   first column in the value field (the left
743 				   `more' indicator should be ON).  This can
744 				   be zero or negative. */
745        int charsToEnd;          /* Number of characters from the cursor to the
746 				   end of the value (including at the cursor
747 				   position). */
748        int leftColChar;         /* Character number at left column of value
749 				   field. */
750        int rightColChar;        /* Character number at right column of value
751 				   field. */
752        int charN;               /* Character number. */
753        /***********************************************************************
754        * Set cursor mode/position.
755        ***********************************************************************/
756        set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
757        set_cursor_mode (CURSORon);
758        /***********************************************************************
759        * Read/process keys until an `exit' key...
760        ***********************************************************************/
761        for (;;) {
762 	  read_input (
763 #if defined(CURSESui)
764 		      PW->wid,
765 #endif
766 			       &(PW->key), PASSTHRUri, TRUE);
767 	  /********************************************************************
768 	  * Check for an `exit' key.
769 	  ********************************************************************/
770 	  for (charN = 0; PW->exitChars[charN] != NUL; charN++) {
771 	     if (PW->key == PW->exitChars[charN]) {
772 	       va_end (ap);
773 	       return TRUE;
774 	     }
775 	  }
776 	  /********************************************************************
777 	  * Check for left arrow.
778 	  ********************************************************************/
779 	  if (PW->key == KB_LEFTARROW) {
780 	    begin_pasteboard_update ();
781 	    if (PW->curCol > PW->vLcol) {
782 	      PW->curChar--;
783 	      PW->curCol--;
784 	    }
785 	    else
786 	      if (PW->curChar > 0) {
787 		PW->curChar--;
788 		charsToEnd = PW->curLen - PW->curChar;
789 		put_chars (PW->wid, &(PW->value[PW->curChar]),
790 			   MINIMUM(charsToEnd,PW->NvCols), PW->vRow, PW->vLcol,
791 			   FALSE, NORMAL);
792 		if (PW->curChar == 0) put_chars (PW->wid, " ", 1, PW->vRow,
793 						 PW->mLcol, FALSE, NORMAL);
794 		offRight = charsToEnd - PW->NvCols;
795 		if (offRight == 1) put_chars (PW->wid, ">", 1, PW->vRow,
796 					      PW->mRcol, FALSE, NORMAL);
797 	      }
798 	      else
799 		ring_bell ();
800 	    end_pasteboard_update ();
801 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
802 	    continue;
803 	  }
804 	  /********************************************************************
805 	  * Check for right arrow.
806 	  ********************************************************************/
807 	  if (PW->key == KB_RIGHTARROW) {
808 	    begin_pasteboard_update ();
809 	    if (PW->curCol < PW->vRcol) {
810 	      if (PW->curChar < PW->curLen) {
811 		PW->curChar++;
812 		PW->curCol++;
813 	      }
814 	      else
815 		ring_bell ();
816 	    }
817 	    else
818 	      if (PW->curChar < PW->curLen) {
819 		PW->curChar++;
820 		leftColChar = PW->curChar - PW->NvCols + 1;
821 		if (PW->curChar == PW->curLen) {
822 		  put_chars (PW->wid, &(PW->value[leftColChar]),
823 			     PW->NvCols - 1, PW->vRow, PW->vLcol, FALSE,
824 			     NORMAL);
825 		  put_chars (PW->wid, " ", 1, PW->vRow, PW->vRcol, FALSE,
826 			     NORMAL);
827 		}
828 		else
829 		  put_chars (PW->wid, &(PW->value[leftColChar]), PW->NvCols,
830 			     PW->vRow, PW->vLcol, FALSE, NORMAL);
831 		put_chars (PW->wid, "<", 1, PW->vRow, PW->mLcol, FALSE,
832 			   NORMAL);
833 		charsToEnd = PW->curLen - PW->curChar;
834 		colsToEnd = PW->vRcol - PW->curCol + 1;
835 		offRight = charsToEnd - colsToEnd;
836 		if (offRight == 0) put_chars (PW->wid, " ", 1, PW->vRow,
837 					      PW->mRcol, FALSE, NORMAL);
838 	      }
839 	      else
840 		ring_bell ();
841 	    end_pasteboard_update ();
842 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
843 	    continue;
844 	  }
845 	  /********************************************************************
846 	  * Check for delete key.
847 	  ********************************************************************/
848 	  if (PW->key == KB_DELETE) {
849 	    begin_pasteboard_update ();
850 	    if (PW->curCol == PW->vLcol) {
851 	      if (PW->curChar > 0) {
852 		charsToEnd = PW->curLen - PW->curChar;
853 		memmove (&PW->value[PW->curChar-1], &PW->value[PW->curChar],
854 			 charsToEnd + 1);
855 		PW->curChar--;
856 		PW->curLen--;
857 		if (PW->curChar == 0) put_chars (PW->wid, " ", 1, PW->vRow,
858 						 PW->mLcol, FALSE, NORMAL);
859 	      }
860 	      else
861 		ring_bell ();
862 	    }
863 	    else {
864 	      charsToEnd = PW->curLen - PW->curChar;
865 	      colsToEnd = PW->vRcol - PW->curCol + 1;
866 	      offRight = charsToEnd - colsToEnd;
867 	      if (offRight > 0) {
868 		memmove (&(PW->value[PW->curChar-1]),
869 			 &(PW->value[PW->curChar]), charsToEnd + 1);
870 		PW->curChar--;
871 		PW->curCol--;
872 		PW->curLen--;
873 		colsToEnd++;
874 		put_chars (PW->wid, &(PW->value[PW->curChar]), colsToEnd,
875 			   PW->vRow, PW->curCol, FALSE, NORMAL);
876 		offRight--;
877 		if (offRight == 0) put_chars (PW->wid, " ", 1, PW->vRow,
878 					      PW->mRcol, FALSE, NORMAL);
879 	      }
880 	      else
881 		if (charsToEnd > 0) {
882 		  memmove (&(PW->value[PW->curChar-1]),
883 			   &(PW->value[PW->curChar]), charsToEnd + 1);
884 		  PW->curChar--;
885 		  PW->curCol--;
886 		  PW->curLen--;
887 		  put_chars (PW->wid, &(PW->value[PW->curChar]), charsToEnd,
888 			     PW->vRow, PW->curCol, FALSE, NORMAL);
889 		  put_chars (PW->wid, " ", 1, PW->vRow,
890 			     PW->curCol + charsToEnd, FALSE, NORMAL);
891 		}
892 		else {
893 		  PW->curChar--;
894 		  PW->value[PW->curChar] = NUL;
895 		  PW->curLen--;
896 		  PW->curCol--;
897 		  put_chars (PW->wid, " ", 1, PW->vRow, PW->curCol, FALSE,
898 			     NORMAL);
899 		}
900 	    }
901 	    end_pasteboard_update ();
902 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
903 	    continue;
904 	  }
905 	  /********************************************************************
906 	  * Check for the `refresh' key.
907 	  ********************************************************************/
908 	  if (PW->key == PW->refreshChar) {
909 	    repaint_screen ();
910 	    continue;
911 	  }
912 	  /********************************************************************
913 	  * Check for the SOL key.  If the current length of the value is
914 	  * greater than the number of available columns, the right `more'
915 	  * indicator is drawn (even though it may already be drawn).
916 	  ********************************************************************/
917 	  if (PW->key == PW->SOLchar) {
918 	    begin_pasteboard_update ();
919 	    leftColChar = PW->curChar - (PW->curCol - PW->vLcol);
920 	    if (leftColChar > 0) {
921 	      put_chars (PW->wid, " ", 1, PW->vRow, PW->mLcol, FALSE, NORMAL);
922 	      put_chars (PW->wid, PW->value, PW->NvCols, PW->vRow, PW->vLcol,
923 			 FALSE, NORMAL);
924 	      if (PW->curLen > PW->NvCols) put_chars (PW->wid, ">", 1,
925 						      PW->vRow, PW->mRcol,
926 						      FALSE, NORMAL);
927 	    }
928 	    PW->curCol = PW->vLcol;
929 	    PW->curChar = 0;
930 	    end_pasteboard_update ();
931 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
932 	    continue;
933 	  }
934 	  /********************************************************************
935 	  * Check for the EOL key.  The `right column character' is used to
936 	  * determine if moving the cursor to the end of the value will cause
937 	  * characters to be off the left end of the value field.  The left
938 	  * and right `more' indicators are updated regardless of how they may
939 	  * have already been.
940 	  ********************************************************************/
941 	  if (PW->key == PW->EOLchar) {
942 	    begin_pasteboard_update ();
943 	    rightColChar = PW->curChar + (PW->vRcol - PW->curCol);
944 	    if (rightColChar < PW->curLen) {
945 	      put_chars (PW->wid, "<", 1, PW->vRow, PW->mLcol, FALSE, NORMAL);
946 	      put_chars (PW->wid, &(PW->value[PW->curLen - PW->NvCols + 1]),
947 			 PW->NvCols - 1, PW->vRow, PW->vLcol, FALSE, NORMAL);
948 	      put_chars (PW->wid, " ", 1, PW->vRow, PW->vRcol, FALSE, NORMAL);
949 	      put_chars (PW->wid, " ", 1, PW->vRow, PW->mRcol, FALSE, NORMAL);
950 	    }
951 	    PW->curCol = MinInt (PW->vLcol + PW->curLen, PW->vRcol);
952 	    PW->curChar = PW->curLen;
953 	    end_pasteboard_update ();
954 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
955 	    continue;
956 	  }
957 	  /********************************************************************
958 	  * Check for the toggle insert/overstrike mode key.
959 	  ********************************************************************/
960 	  if (PW->key == PW->TOGGLEchar) {
961 	    PW->insertMode = (PW->insertMode ? FALSE : TRUE);
962 	    continue;
963 	  }
964 	  /********************************************************************
965 	  * Check for a printable character to insert/overstrike.
966 	  ********************************************************************/
967 	  if (Printable(PW->key)) {
968 	    begin_pasteboard_update ();
969 	    charsToEnd = PW->curLen - PW->curChar;
970 	    colsToEnd = PW->vRcol - PW->curCol + 1;
971 	    if (charsToEnd == 0) {
972 	      /****************************************************************
973 	      * The cursor is at the end of the value.
974 	      ****************************************************************/
975 	      if (PW->curLen < PW->maxChars) {
976 		catchrX (PW->value, PW->key, PW->maxChars);
977 		PW->curLen++;
978 		PW->curChar++;
979 		if (colsToEnd > 1) {
980 		  put_chars (PW->wid, &(PW->value[PW->curChar-1]), 1, PW->vRow,
981 			     PW->curCol, FALSE, NORMAL);
982 		  PW->curCol++;
983 		}
984 		else {
985 		  leftColChar = PW->curChar - PW->NvCols + 1;
986 		  put_chars (PW->wid, &(PW->value[leftColChar]), PW->NvCols-1,
987 			     PW->vRow, PW->vLcol, FALSE, NORMAL);
988 		  offLeft = PW->curLen - PW->NvCols + 1;
989 		  if (offLeft == 1) put_chars (PW->wid, "<", 1, PW->vRow,
990 					       PW->mLcol, FALSE, NORMAL);
991 		}
992 	      }
993 	      else
994 		ring_bell ();
995 	    }
996 	    else {
997 	      /****************************************************************
998 	      * The cursor is somewhere in the middle of the value.
999 	      ****************************************************************/
1000 	      if (!PW->insertMode ||
1001 		  (PW->insertMode && PW->curLen < PW->maxChars)) {
1002 		if (PW->insertMode) {
1003 		  /************************************************************
1004 		  * Insert mode.
1005 		  ************************************************************/
1006 		  memmove (&(PW->value[PW->curChar+1]),
1007 			   &(PW->value[PW->curChar]), charsToEnd + 1);
1008 		  PW->value[PW->curChar] = PW->key;
1009 		  PW->curLen++;
1010 		  if (colsToEnd > 1) {
1011 		    /**********************************************************
1012 		    * The cursor is not in the last column of the value field.
1013 		    **********************************************************/
1014 		    put_chars (PW->wid, &(PW->value[PW->curChar]),
1015 			       MINIMUM(colsToEnd,charsToEnd + 1),
1016 			       PW->vRow, PW->curCol, FALSE, NORMAL);
1017 		    PW->curChar++;
1018 		    PW->curCol++;
1019 		    charsToEnd = PW->curLen - PW->curChar;
1020 		    colsToEnd = PW->vRcol - PW->curCol + 1;
1021 		    offRight = charsToEnd - colsToEnd;
1022 		    if (offRight == 1) put_chars (PW->wid, ">", 1, PW->vRow,
1023 						  PW->mRcol, FALSE, NORMAL);
1024 		  }
1025 		  else {
1026 		    /**********************************************************
1027 		    * The cursor is in the last column of the value field.
1028 		    **********************************************************/
1029 		    PW->curChar++;
1030 		    leftColChar = PW->curChar - PW->NvCols + 1;
1031 		    put_chars (PW->wid, &(PW->value[leftColChar]), PW->NvCols,
1032 			       PW->vRow, PW->vLcol, FALSE, NORMAL);
1033 		    if (leftColChar == 1) put_chars (PW->wid, "<", 1, PW->vRow,
1034 						     PW->mLcol, FALSE, NORMAL);
1035 		  }
1036 		}
1037 		else {
1038 		  /************************************************************
1039 		  * Overstrike mode.
1040 		  ************************************************************/
1041 		  PW->value[PW->curChar] = PW->key;
1042 		  if (colsToEnd > 1) {
1043 		    /**********************************************************
1044 		    * The cursor is not in the last column of the value field.
1045 		    **********************************************************/
1046 		    put_chars (PW->wid, &(PW->value[PW->curChar]), 1, PW->vRow,
1047 			       PW->curCol, FALSE, NORMAL);
1048 		    PW->curChar++;
1049 		    PW->curCol++;
1050 		  }
1051 		  else {
1052 		    /**********************************************************
1053 		    * The cursor is in the last column of the value field.
1054 		    **********************************************************/
1055 		    offRight = charsToEnd - 1;
1056 		    if (offRight > 0) {
1057 		      /********************************************************
1058 		      * One or more character off the right end.
1059 		      ********************************************************/
1060 		      PW->curChar++;
1061 		      leftColChar = PW->curChar - PW->NvCols + 1;
1062 		      put_chars (PW->wid, &(PW->value[leftColChar]),
1063 				 PW->NvCols, PW->vRow, PW->vLcol, FALSE,
1064 				 NORMAL);
1065 		      if (leftColChar == 1) put_chars (PW->wid, "<", 1,
1066 						       PW->vRow, PW->mLcol,
1067 						       FALSE, NORMAL);
1068 		      offRight--;
1069 		      if (offRight == 0) put_chars (PW->wid, " ", 1, PW->vRow,
1070 						    PW->mRcol, FALSE, NORMAL);
1071 		    }
1072 		    else {
1073 		      /********************************************************
1074 		      * Cursor is on the last character.
1075 		      ********************************************************/
1076 		      PW->curChar++;
1077 		      leftColChar = PW->curChar - PW->NvCols + 1;
1078 		      put_chars (PW->wid, &(PW->value[leftColChar]),
1079 				 PW->NvCols - 1, PW->vRow, PW->vLcol, FALSE,
1080 				 NORMAL);
1081 		      put_chars (PW->wid, " ", 1, PW->vRow, PW->vRcol, FALSE,
1082 				 NORMAL);
1083 		      if (leftColChar == 1) put_chars (PW->wid, "<", 1,
1084 						       PW->vRow, PW->mLcol,
1085 						       FALSE, NORMAL);
1086 		    }
1087 		  }
1088 		}
1089 	      }
1090 	      else
1091 		ring_bell ();
1092 	    }
1093 	    end_pasteboard_update ();
1094 	    set_cursor_abs (PW->wid, PW->vRow, PW->curCol);
1095 	    continue;
1096 	  }
1097 	  /********************************************************************
1098 	  * None of the above, illegal character.
1099 	  ********************************************************************/
1100 	  ring_bell ();
1101        }
1102      }
1103    }
1104    va_end (ap);
1105    return FALSE;                                      /* Illegal operation. */
1106 }
1107 
1108 /******************************************************************************
1109 * EditWindow.
1110 ******************************************************************************/
1111 
1112 #if defined(STDARG)
EditWindow(int opT,...)1113 int EditWindow (int opT, ...)
1114 #else
1115 int EditWindow (va_alist)
1116 va_dcl
1117 #endif
1118 {
1119    int op;                       /* Operation to perform (eg. create new
1120 				    edit window, delete window, etc.). */
1121    struct EditWindowStruct *EW;  /* Edit window configuration. */
1122    va_list ap;
1123    /***************************************************************************
1124    * Start variable-length argument list scanning.
1125    ***************************************************************************/
1126 #if defined(STDARG)
1127    va_start (ap, opT);
1128    op = opT;
1129 #else
1130    VA_START (ap);
1131    op = va_arg (ap, int);
1132 #endif
1133    EW = va_arg (ap, struct EditWindowStruct *);
1134    /***************************************************************************
1135    * Perform desired operation.  Some operations wait for user input (by
1136    * falling through the `switch' statement).
1137    ***************************************************************************/
1138    switch (op) {
1139      /*************************************************************************
1140      * Check for new menu.
1141      *************************************************************************/
1142      case NEWew:
1143      case UPDATEew: {
1144        /***********************************************************************
1145        * Get remaining arguments.
1146        ***********************************************************************/
1147        EW->insertMode = va_arg (ap, Logical);
1148        va_end (ap);
1149        /***********************************************************************
1150        * Create window?
1151        ***********************************************************************/
1152        begin_pasteboard_update ();
1153        if (op == NEWew) {
1154 	 EW->nRowsTotal = BOO(EW->NhLines > 0,1,0) + EW->NhLines +
1155 			  BOO(EW->NeRows > 0,1,0) + EW->NeRows +
1156 			  BOO(EW->NtLines > 0,1,0) + EW->NtLines + 1;
1157 	 create_virtual_display (EW->nRowsTotal, EW->nColsTotal, &(EW->wid),
1158 				 BORDER, NORMAL);
1159 	 paste_virtual_display (EW->wid, EW->ULrow, EW->ULcol);
1160 	 EW->nColS = EW->nColsTotal - 2;
1161        }
1162        /***********************************************************************
1163        * If specified, write label to window.  If this is an update operation,
1164        * first overwrite the existing label.
1165        ***********************************************************************/
1166        if (op == UPDATEew) draw_horizontal_line (EW->wid, 0, 1, EW->nColS,
1167 						 NORMAL, FALSE);
1168        if (EW->label != NULL) {
1169 	 int len = (int) strlen(EW->label);
1170 	 if (len <= EW->nColS) {
1171 	   int nRemainChars = EW->nColS - len;
1172 	   int nCharsBefore = nRemainChars / 2;
1173 	   put_chars (EW->wid, EW->label, len, 0, nCharsBefore + 1, FALSE,
1174 		      REVERSE1);
1175 	 }
1176        }
1177        /***********************************************************************
1178        * If necessary, calculate positions and draw horizontal lines on window.
1179        ***********************************************************************/
1180        if (op == NEWew) {
1181 	 int nSections = 0;    /* Number of sections in the window. */
1182 	 int xRow = 1;         /* Start at row 1 (row 0 is top border line). */
1183 	 if (EW->NhLines > 0) {
1184 	   nSections++;
1185 	   EW->hRowT = xRow;
1186 	   EW->hRowB = EW->hRowT + EW->NhLines - 1;
1187 	   xRow += EW->NhLines + 1;
1188 	 }
1189 	 if (EW->NeRows > 0) {
1190 	   nSections++;
1191 	   EW->eRowT = xRow;
1192 	   EW->eRowB = EW->eRowT + EW->NeRows - 1;
1193 	   xRow += EW->NeRows + 1;
1194 	   if (nSections > 1) draw_horizontal_line (EW->wid, EW->eRowT - 1, 0,
1195 						    EW->nColsTotal - 1, NORMAL,
1196 						    TRUE);
1197 	 }
1198 	 if (EW->NtLines > 0) {
1199 	   nSections++;
1200 	   EW->tRowT = xRow;
1201 	   EW->tRowB = EW->tRowT + EW->NtLines - 1;
1202 	   xRow += EW->NtLines + 1;
1203 	   if (nSections > 1) draw_horizontal_line (EW->wid, EW->tRowT - 1, 0,
1204 						    EW->nColsTotal - 1, NORMAL,
1205 						    TRUE);
1206 	 }
1207        }
1208        /***********************************************************************
1209        * If specified, write header section to window.  If an update operation,
1210        * it is assumed that the number of lines has not changed.
1211        ***********************************************************************/
1212        if (EW->NhLines > 0) DrawSection (EW->wid, EW->hRowT, EW->hRowB, 0,
1213 					 EW->NhLines, EW->hLines, EW->nColS);
1214        /***********************************************************************
1215        * Write edit section to window.  If an update operation, note that the
1216        * text may have changed (to more or less lines) but not the number of
1217        * rows.
1218        ***********************************************************************/
1219        EW->nChars = (int) strlen(EW->eText);
1220        EW->xChars = EW->nChars;
1221        EW->cursorRow = EW->eRowT;
1222        EW->cursorCol = 1;
1223        EW->firstChar = 0;
1224        EW->cursorChar = 0;
1225        DrawEditSection (EW);
1226        /*********************************************************************
1227        * Setup/display initial percentage indicator.
1228        *********************************************************************/
1229        if (EW->ePct) {
1230 	 if (op == NEWew) {
1231 	   EW->ePctRowN = EW->eRowB + 1;
1232 	   EW->ePctColN = EW->nColsTotal - 7;
1233 	 }
1234 	 UpdatePctString (EW->wid, EW->nChars + 1, 1, EW->cursorChar,
1235 			  EW->ePctRowN, EW->ePctColN);
1236        }
1237        /***********************************************************************
1238        * If specified, write trailer section to window.  If an update
1239        * operation, it is assumed that the number of lines has not changed.
1240        ***********************************************************************/
1241        if (EW->NtLines > 0) DrawSection (EW->wid, EW->tRowT, EW->tRowB, 0,
1242 					 EW->NtLines, EW->tLines, EW->nColS);
1243        /***********************************************************************
1244        * Update screen.
1245        ***********************************************************************/
1246        end_pasteboard_update ();
1247        /***********************************************************************
1248        * Position cursor (which must occur after the pasteboard batching is
1249        * ended).
1250        ***********************************************************************/
1251        set_cursor_abs (EW->wid, EW->cursorRow, EW->cursorCol);
1252        set_cursor_mode (CURSORon);
1253        return TRUE;
1254      }
1255      /*************************************************************************
1256      * Check for a beep request.
1257      *************************************************************************/
1258      case BEEPew:
1259        ring_bell ();
1260        va_end (ap);
1261        return TRUE;
1262      /*************************************************************************
1263      * Check if menu should be deleted.
1264      *************************************************************************/
1265      case DELETEew:
1266        delete_virtual_display (EW->wid);
1267        va_end (ap);
1268        return TRUE;
1269      /*************************************************************************
1270      * Get next input (exit key).
1271      *************************************************************************/
1272      case READew: {
1273        int length, i, count;
1274        set_cursor_abs (EW->wid, EW->cursorRow, EW->cursorCol);
1275        set_cursor_mode (CURSORon);
1276        for (;;) {
1277 	  read_input (
1278 #if defined(CURSESui)
1279 		      EW->wid,
1280 #endif
1281 			       &(EW->key), PASSTHRUri, TRUE);
1282 	  /********************************************************************
1283 	  * Check for an exit key.  If no exit keys were specified, any key
1284 	  * causes an exit.
1285 	  ********************************************************************/
1286 	  if (EW->exitChars[0] == NUL) {
1287 	    va_end (ap);
1288 	    return TRUE;
1289 	  }
1290 	  else {
1291 	    int charN;
1292 	    for (charN = 0; EW->exitChars[charN] != NUL; charN++) {
1293 	       if (EW->key == EW->exitChars[charN]) {
1294 		 va_end (ap);
1295 		 return TRUE;
1296 	       }
1297 	    }
1298 	  }
1299 	  /********************************************************************
1300 	  * Check for start-of-line key.
1301 	  ********************************************************************/
1302 	  if (EW->key == EW->SOLkey) {
1303 	    ring_bell ();
1304 	    continue;
1305 	  }
1306 	  /********************************************************************
1307 	  * Check for end-of-line key.
1308 	  ********************************************************************/
1309 	  if (EW->key == EW->EOLkey) {
1310 	    ring_bell ();
1311 	    continue;
1312 	  }
1313 	  /********************************************************************
1314 	  * Check for start-of-text key.
1315 	  ********************************************************************/
1316 	  if (EW->key == EW->SOTkey) {
1317 	    ring_bell ();
1318 	    continue;
1319 	  }
1320 	  /********************************************************************
1321 	  * Check for end-of-text key.
1322 	  ********************************************************************/
1323 	  if (EW->key == EW->EOTkey) {
1324 	    ring_bell ();
1325 	    continue;
1326 	  }
1327 	  /********************************************************************
1328 	  * Check for next-word key.
1329 	  ********************************************************************/
1330 	  if (EW->key == EW->NWkey) {
1331 	    ring_bell ();
1332 	    continue;
1333 	  }
1334 	  /********************************************************************
1335 	  * Check for delete-line key.
1336 	  ********************************************************************/
1337 	  if (EW->key == EW->DLkey) {
1338 	    if (EW->readOnly)
1339 	      ring_bell ();
1340 	    else {
1341 	      ring_bell ();
1342 	    }
1343 	    continue;
1344 	  }
1345 	  /********************************************************************
1346 	  * Check for next-screen key.
1347 	  ********************************************************************/
1348 	  if (EW->key == EW->NSkey) {
1349 	    if (EW->eText[EW->cursorChar] == NUL)
1350 	      ring_bell ();
1351 	    else {
1352 	      int count;       /* Number of lines that cursor is moved down. */
1353 	      /****************************************************************
1354 	      * Move the cursor to the beginning of the line.
1355 	      ****************************************************************/
1356 	      while (EW->cursorChar > 0) {
1357 		if (EW->eText[EW->cursorChar-1] == Nl) break;
1358 		EW->cursorChar--;
1359 	      }
1360 	      /****************************************************************
1361 	      * Move the cursor down the number of rows displayed (or less).
1362 	      ****************************************************************/
1363 	      for (count = 0;
1364 		   EW->eText[EW->cursorChar] != NUL;
1365 		   EW->cursorChar++) {
1366 		 if (EW->eText[EW->cursorChar] == Nl) {
1367 		   count++;
1368 		   if (count == EW->NeRows) {
1369 		     EW->cursorChar++;
1370 		     break;
1371 		   }
1372 		 }
1373 	      }
1374 	      /****************************************************************
1375 	      * Move the `first character displayed' down the same number of
1376 	      * rows (if necessary).
1377 	      ****************************************************************/
1378 	      if (EW->cursorRow + count > EW->eRowB) {
1379 		int count1;    /* Number of lines after the first displayed. */
1380 		int charN;
1381 		for (count1 = 0, charN = EW->firstChar;
1382 		     EW->eText[charN] != NUL; charN++) {
1383 		   if (EW->eText[charN] == Nl) count1++;
1384 		}
1385 		count1 -= (EW->NeRows - 1);
1386 		count1 = MINIMUM(count1,count);
1387 		while (count1 > 0) {
1388 		  if (EW->eText[EW->firstChar] == Nl) count1--;
1389 		  EW->firstChar++;
1390 		}
1391 		DrawEditSection (EW);
1392 	      }
1393 	      /****************************************************************
1394 	      * Update the percentage and cursor position.
1395 	      ****************************************************************/
1396 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1397 					     EW->cursorChar, EW->ePctRowN,
1398 					     EW->ePctColN);
1399 	      SetEditCursor (EW);
1400 	    }
1401 	    continue;
1402 	  }
1403 	  /********************************************************************
1404 	  * Check for prev-screen key.
1405 	  ********************************************************************/
1406 	  if (EW->key == EW->PSkey) {
1407 	    if (EW->cursorChar == 0)
1408 	      ring_bell ();
1409 	    else {
1410 	      int count;       /* Number of lines that cursor is moved up. */
1411 	      /****************************************************************
1412 	      * Move the cursor to the beginning of the line.
1413 	      ****************************************************************/
1414 	      while (EW->cursorChar > 0) {
1415 		if (EW->eText[EW->cursorChar-1] == Nl) break;
1416 		EW->cursorChar--;
1417 	      }
1418 	      /****************************************************************
1419 	      * Move the cursor up the number of rows displayed (or less).
1420 	      ****************************************************************/
1421 	      for (count = 0; EW->cursorChar > 0; EW->cursorChar--) {
1422 		 if (EW->eText[EW->cursorChar] == Nl) {
1423 		   count++;
1424 		   if (count == EW->NeRows) {
1425 		     if (EW->cursorChar > 0) {
1426 		       EW->cursorChar--;
1427 		       while (EW->cursorChar > 0) {
1428 			 if (EW->eText[EW->cursorChar-1] == Nl) break;
1429 			 EW->cursorChar--;
1430 		       }
1431 		     }
1432 		     break;
1433 		   }
1434 		 }
1435 	      }
1436 	      /****************************************************************
1437 	      * Move the `first character displayed' up the same number of
1438 	      * rows (if necessary).
1439 	      ****************************************************************/
1440 	      if (EW->cursorRow - count < EW->eRowT) {
1441 		while (EW->firstChar > 0) {
1442 		  if (EW->eText[EW->firstChar] == Nl) {
1443 		    count--;
1444 		    if (count == 0) {
1445 		      if (EW->firstChar > 0) {
1446 			EW->firstChar--;
1447 			while (EW->firstChar > 0) {
1448 			  if (EW->eText[EW->firstChar-1] == Nl) break;
1449 			  EW->firstChar--;
1450 			}
1451 		      }
1452 		      break;
1453 		    }
1454 		  }
1455 		  EW->firstChar--;
1456 		}
1457 		DrawEditSection (EW);
1458 	      }
1459 	      /****************************************************************
1460 	      * Update the percentage and cursor position.
1461 	      ****************************************************************/
1462 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1463 					     EW->cursorChar, EW->ePctRowN,
1464 					     EW->ePctColN);
1465 	      SetEditCursor (EW);
1466 	    }
1467 	    continue;
1468 	  }
1469 	  /********************************************************************
1470 	  * Check for down arrow key.
1471 	  ********************************************************************/
1472 	  if (EW->key == KB_DOWNARROW) {
1473 	    char *nextNL = strchr (&(EW->eText[EW->cursorChar]), Nl);
1474 	    if (nextNL == NULL)
1475 	      ring_bell ();
1476 	    else {
1477 	      int toNL = (int) (nextNL - &(EW->eText[EW->cursorChar]));
1478 	      EW->cursorChar += (toNL + 1);
1479 	      for (i = 1; i < EW->cursorCol; i++) {
1480 		 if (EW->eText[EW->cursorChar] == Nl) break;
1481 		 if (EW->eText[EW->cursorChar] == NUL) break;
1482 		 EW->cursorChar++;
1483 	      }
1484 	      if (EW->cursorRow == EW->eRowB) {
1485 		while (EW->eText[EW->firstChar] != Nl) EW->firstChar++;
1486 		EW->firstChar++;
1487 		DrawEditSection (EW);
1488 	      }
1489 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1490 					     EW->cursorChar, EW->ePctRowN,
1491 					     EW->ePctColN);
1492 	      SetEditCursor (EW);
1493 	    }
1494 	    continue;
1495 	  }
1496 	  /********************************************************************
1497 	  * Check for up arrow key.
1498 	  ********************************************************************/
1499 	  if (EW->key == KB_UPARROW) {
1500 	    if (EW->firstChar == 0 && EW->cursorRow == EW->eRowT)
1501 	      ring_bell ();
1502 	    else {
1503 	      /****************************************************************
1504 	      * If the cursor is on the top line then scroll up one line.
1505 	      ****************************************************************/
1506 	      if (EW->cursorRow == EW->eRowT) {
1507 		for (EW->firstChar--; EW->firstChar > 0; EW->firstChar--) {
1508 		   if (EW->eText[EW->firstChar-1] == Nl) break;
1509 		}
1510 		DrawEditSection (EW);
1511 	      }
1512 	      /****************************************************************
1513 	      * Set the cursor character as follows...
1514 	      * 1. Move backward to the newline character of the preceeding
1515 	      *    line while counting the number of characters moved.
1516 	      * 2. Move backward to the first character of the preceeding
1517 	      *    line.
1518 	      * 3. Move forward to position the cursor character at the same
1519 	      *    column or at the newline character (which ever occurs
1520 	      *    first).
1521 	      ****************************************************************/
1522 	      for (count = 1, EW->cursorChar--;
1523 		   EW->eText[EW->cursorChar] != Nl;
1524 		   EW->cursorChar--) count++;
1525 	      while (EW->cursorChar > 0) {
1526 		if (EW->eText[EW->cursorChar-1] == Nl) break;
1527 		EW->cursorChar--;
1528 	      }
1529 	      for (i = 1; i < count; i++) {
1530 		 if (EW->eText[EW->cursorChar] == Nl) break;
1531 		 EW->cursorChar++;
1532 	      }
1533 	      /****************************************************************
1534 	      * Update the percentage and cursor position.
1535 	      ****************************************************************/
1536 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1537 					     EW->cursorChar, EW->ePctRowN,
1538 					     EW->ePctColN);
1539 	      SetEditCursor (EW);
1540 	    }
1541 	    continue;
1542 	  }
1543 	  /********************************************************************
1544 	  * Check for left arrow key.
1545 	  ********************************************************************/
1546 	  if (EW->key == KB_LEFTARROW) {
1547 	    if (EW->cursorChar == 0)
1548 	      ring_bell ();
1549 	    else {
1550 	      if (EW->cursorChar == EW->firstChar) {
1551 		for (EW->firstChar--; EW->firstChar > 0; EW->firstChar--) {
1552 		   if (EW->eText[EW->firstChar-1] == Nl) break;
1553 		}
1554 		DrawEditSection (EW);
1555 	      }
1556 	      EW->cursorChar--;
1557 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1558 					     EW->cursorChar, EW->ePctRowN,
1559 					     EW->ePctColN);
1560 	      SetEditCursor (EW);
1561 	    }
1562 	    continue;
1563 	  }
1564 	  /********************************************************************
1565 	  * Check for right arrow key.
1566 	  ********************************************************************/
1567 	  if (EW->key == KB_RIGHTARROW) {
1568 	    if (EW->eText[EW->cursorChar] == NUL)
1569 	      ring_bell ();
1570 	    else {
1571 	      if (EW->cursorRow == EW->eRowB &&
1572 		  EW->eText[EW->cursorChar] == Nl) {
1573 		while (EW->eText[EW->firstChar] != Nl) EW->firstChar++;
1574 		EW->firstChar++;
1575 		DrawEditSection (EW);
1576 	      }
1577 	      EW->cursorChar++;
1578 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1579 					     EW->cursorChar, EW->ePctRowN,
1580 					     EW->ePctColN);
1581 	      SetEditCursor (EW);
1582 	    }
1583 	    continue;
1584 	  }
1585 	  /********************************************************************
1586 	  * Check for the RETURN key.
1587 	  ********************************************************************/
1588 	  if (EW->key == KB_RETURN) {
1589 	    if (EW->readOnly)
1590 	      ring_bell ();
1591 	    else {
1592 	      if (EW->nChars == EW->xChars) {
1593 		EW->xChars += EW->nColS;
1594 		EW->eText = cdf_ReallocateMemory (EW->eText,
1595 					      (size_t) (EW->xChars + 1),
1596 					      FatalError);
1597 	      }
1598 	      length = (int) strlen (&(EW->eText[EW->cursorChar]));
1599 	      memmove (&(EW->eText[EW->cursorChar+1]),
1600 		       &(EW->eText[EW->cursorChar]), (size_t) (length + 1));
1601 	      EW->eText[EW->cursorChar] = Nl;
1602 	      EW->nChars++;
1603 	      EW->cursorChar++;
1604 	      if (EW->cursorRow == EW->eRowB) {
1605 		while (EW->eText[EW->firstChar] != Nl) EW->firstChar++;
1606 		EW->firstChar++;
1607 	      }
1608 	      DrawEditSection (EW);
1609 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1610 					     EW->cursorChar, EW->ePctRowN,
1611 					     EW->ePctColN);
1612 	      SetEditCursor (EW);
1613 	    }
1614 	    continue;
1615 	  }
1616 	  /********************************************************************
1617 	  * Check for the DELETE key.
1618 	  ********************************************************************/
1619 	  if (EW->key == KB_DELETE) {
1620 	    if (EW->readOnly)
1621 	      ring_bell ();
1622 	    else {
1623 	      if (EW->cursorChar == 0)
1624 		ring_bell ();
1625 	      else {
1626 		length = (int) strlen (&(EW->eText[EW->cursorChar]));
1627 		memmove (&(EW->eText[EW->cursorChar-1]),
1628 			 &(EW->eText[EW->cursorChar]), (size_t) (length + 1));
1629 		EW->nChars--;
1630 		if (EW->cursorChar == EW->firstChar) {
1631 		  for (EW->firstChar--; EW->firstChar > 0; EW->firstChar--) {
1632 		     if (EW->eText[EW->firstChar-1] == Nl) break;
1633 		  }
1634 		}
1635 		EW->cursorChar--;
1636 		DrawEditSection (EW);
1637 		if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1638 					       EW->cursorChar, EW->ePctRowN,
1639 					       EW->ePctColN);
1640 		SetEditCursor (EW);
1641 	      }
1642 	    }
1643 	    continue;
1644 	  }
1645 	  /********************************************************************
1646 	  * Check for the toggle insert/overstrike mode key.
1647 	  ********************************************************************/
1648 	  if (EW->key == EW->TOGGLEkey) {
1649 	    if (EW->readOnly)
1650 	      ring_bell ();
1651 	    else
1652 	      EW->insertMode = BOO(EW->insertMode,FALSE,TRUE);
1653 	    continue;
1654 	  }
1655 	  /********************************************************************
1656 	  * Check for the `refresh' key.
1657 	  ********************************************************************/
1658 	  if (EW->key == EW->REFRESHkey) {
1659 	    repaint_screen ();
1660 	    continue;
1661 	  }
1662 	  /********************************************************************
1663 	  * Check for a printable character.
1664 	  ********************************************************************/
1665 	  if (Printable(EW->key)) {
1666 	    if (EW->readOnly)
1667 	      ring_bell ();
1668 	    else {
1669 	      if (EW->insertMode ||
1670 		  EW->eText[EW->cursorChar] == Nl ||    /* If over- */
1671 		  EW->eText[EW->cursorChar] == NUL) {   /* strike mode. */
1672 		if (EW->nChars == EW->xChars) {
1673 		  EW->xChars += EW->nColS;
1674 		  EW->eText = cdf_ReallocateMemory (EW->eText,
1675 					       (size_t) (EW->xChars + 1),
1676 					       FatalError);
1677 		}
1678 		length = (int) strlen (&(EW->eText[EW->cursorChar]));
1679 		memmove (&(EW->eText[EW->cursorChar+1]),
1680 			 &(EW->eText[EW->cursorChar]), (size_t) (length + 1));
1681 		EW->eText[EW->cursorChar] = (char) EW->key;
1682 		EW->nChars++;
1683 	      }
1684 	      else
1685 		EW->eText[EW->cursorChar] = (char) EW->key;
1686 	      DrawEditSection (EW);
1687 	      if (EW->cursorCol < EW->nColS) EW->cursorChar++;
1688 	      if (EW->ePct) UpdatePctString (EW->wid, EW->nChars + 1, 1,
1689 					     EW->cursorChar, EW->ePctRowN,
1690 					     EW->ePctColN);
1691 	      SetEditCursor (EW);
1692 	    }
1693 	    continue;
1694 	  }
1695 	  /********************************************************************
1696 	  * Illegal key, ring the bell.
1697 	  ********************************************************************/
1698 	  ring_bell ();
1699        }
1700      }
1701    }
1702    va_end (ap);
1703    return FALSE;                                      /* Illegal operation. */
1704 }
1705 
1706 /******************************************************************************
1707 * DrawEditSection.
1708 ******************************************************************************/
1709 
DrawEditSection(EW)1710 static void DrawEditSection (EW)
1711 struct EditWindowStruct *EW;
1712 {
1713   char *charPtr = &(EW->eText[EW->firstChar]);
1714   int rowN = EW->eRowT, length;
1715   for (;;) {
1716      char *nextNL = strchr (charPtr, Nl);
1717      if (nextNL == NULL) {
1718        length = (int) strlen (charPtr);
1719        if (length > 0) {
1720 	 put_chars (EW->wid, charPtr, MINIMUM(length,EW->nColS), rowN, 1,
1721 		    FALSE, NORMAL);
1722 	 if (length < EW->nColS) erase_display (EW->wid, rowN, length + 1,
1723 						rowN, EW->nColS);
1724 	 rowN++;
1725        }
1726        break;
1727      }
1728      length = (int) (nextNL - charPtr);
1729      put_chars (EW->wid, charPtr, MINIMUM(length,EW->nColS), rowN, 1, FALSE,
1730 		NORMAL);
1731      if (length < EW->nColS) erase_display (EW->wid, rowN, length + 1,
1732 					    rowN, EW->nColS);
1733      rowN++;
1734      if (rowN > EW->eRowB) break;
1735      charPtr = nextNL + 1;
1736   }
1737   if (rowN <= EW->eRowB) erase_display (EW->wid, rowN, 1,
1738 					EW->eRowB, EW->nColS);
1739   return;
1740 }
1741 
1742 /******************************************************************************
1743 * SetEditCursor.
1744 ******************************************************************************/
1745 
SetEditCursor(EW)1746 static void SetEditCursor (EW)
1747 struct EditWindowStruct *EW;
1748 {
1749   int charN;
1750   EW->cursorRow = EW->eRowT;
1751   EW->cursorCol = 0;
1752   for (charN = EW->firstChar; charN <= EW->cursorChar; charN++) {
1753      EW->cursorCol++;
1754      if (EW->eText[charN] == Nl && charN < EW->cursorChar) {
1755        EW->cursorCol = 0;
1756        EW->cursorRow++;
1757      }
1758   }
1759   set_cursor_abs (EW->wid, EW->cursorRow, EW->cursorCol);
1760   return;
1761 }
1762 
1763 /******************************************************************************
1764 * FieldWindow.
1765 *
1766 *       Header section (optional).
1767 *       Fields section (scrollable) containing one or more fields.
1768 *       Trailer section (optional).
1769 *
1770 ******************************************************************************/
1771 
1772 #if defined(STDARG)
FieldWindow(int opT,...)1773 int FieldWindow (int opT, ...)
1774 #else
1775 int FieldWindow (va_alist)
1776 va_dcl
1777 #endif
1778 {
1779    int op;              /* Operation to perform (eg. create new menu). */
1780    struct FieldWindowStruct *FW;
1781 			/* Menu configuration. */
1782    va_list ap;
1783    /***************************************************************************
1784    * Start variable-length argument list scanning.
1785    ***************************************************************************/
1786 #if defined(STDARG)
1787    va_start (ap, opT);
1788    op = opT;
1789 #else
1790    VA_START (ap);
1791    op = va_arg (ap, int);
1792 #endif
1793    FW = va_arg (ap, struct FieldWindowStruct *);
1794    /***************************************************************************
1795    * Perform desired operation.  Some operations wait for user input (by
1796    * falling through the `switch' statement).
1797    ***************************************************************************/
1798    switch (op) {
1799      /*************************************************************************
1800      * Check for new/modified menu.
1801      *************************************************************************/
1802      case NEWfw:
1803      case UPDATEfw: {
1804        int nSections;           /* Number of sections on window. */
1805        int nHorizLines;         /* Number of horizontal lines needed.  At
1806 				   most 2 horizontal lines will be needed
1807 				   to separate the 3 sections. */
1808        int horizRows[2];        /* Rows at which to draw the horizontal
1809 				   lines. */
1810        int horizLineN;          /* Horizontal line number. */
1811        int xRow;                /* Used when determining starting rows for
1812 				   the various sections. */
1813        /***********************************************************************
1814        * Get remaining arguments.
1815        ***********************************************************************/
1816        FW->fieldN = va_arg (ap, int);
1817        FW->insert = va_arg (ap, Logical);
1818        va_end (ap);
1819        /***********************************************************************
1820        * Validate menu.
1821        ***********************************************************************/
1822        if (FW->NfRows < 1) return FALSE;
1823        /***********************************************************************
1824        * Turn on cursor.
1825        ***********************************************************************/
1826        set_cursor_mode (CURSORon);
1827        /***********************************************************************
1828        * Calculate positions.
1829        ***********************************************************************/
1830        if (op == NEWfw) {
1831 	 FW->nColS = FW->nColsTotal - 2;
1832 	 nSections = 0;
1833 	 nHorizLines = 0;
1834 	 /*********************************************************************
1835 	 * Start at row 1 (row 0 is top border line).
1836 	 *********************************************************************/
1837 	 xRow = 1;
1838 	 if (FW->NhLines > 0) {
1839 	   nSections++;
1840 	   FW->hRowT = xRow;
1841 	   FW->hRowB = FW->hRowT + FW->NhLines - 1;
1842 	   xRow += FW->NhLines + 1;
1843 	 }
1844 	 nSections++;
1845 	 FW->fRowT = xRow;
1846 	 FW->fRowB = FW->fRowT + FW->NfRows - 1;
1847 	 xRow += FW->NfRows + 1;
1848 	 if (nSections > 1) horizRows[nHorizLines++] = FW->fRowT - 1;
1849 	 if (FW->NtLines > 0) {
1850 	   nSections++;
1851 	   FW->tRowT = xRow;
1852 	   FW->tRowB = FW->tRowT + FW->NtLines - 1;
1853 	   xRow += FW->NtLines + 1;
1854 	   if (nSections > 1) horizRows[nHorizLines++] = FW->tRowT - 1;
1855 	 }
1856 	 FW->nRowsTotal = xRow;
1857        }
1858        /***********************************************************************
1859        * Create window?
1860        ***********************************************************************/
1861        begin_pasteboard_update ();
1862        if (op == NEWfw) {
1863 	 create_virtual_display (FW->nRowsTotal, FW->nColsTotal, &(FW->wid),
1864 				 BORDER, NORMAL);
1865 	 paste_virtual_display (FW->wid, FW->ULrow, FW->ULcol);
1866        }
1867        /***********************************************************************
1868        * If specified, write label to window.  If this is an update operation,
1869        * first overwrite the existing label.
1870        ***********************************************************************/
1871        if (op == UPDATEfw) draw_horizontal_line (FW->wid, 0, 1, FW->nColS,
1872 						 NORMAL, FALSE);
1873        if (FW->label != NULL) {
1874 	 int len = (int) strlen(FW->label);
1875 	 if (len <= FW->nColS) {
1876 	   int nRemainChars = FW->nColS - len;
1877 	   int nCharsBefore = nRemainChars / 2;
1878 	   put_chars (FW->wid, FW->label, len, 0, nCharsBefore + 1, FALSE,
1879 		      REVERSE1);
1880 	 }
1881        }
1882        /***********************************************************************
1883        * If necessary, draw horizontal lines on window.
1884        ***********************************************************************/
1885        if (op == NEWfw) {
1886 	 for (horizLineN = 0; horizLineN < nHorizLines; horizLineN++) {
1887 	    draw_horizontal_line (FW->wid, horizRows[horizLineN], 0,
1888 				  FW->nColsTotal-1, NORMAL, TRUE);
1889 	 }
1890        }
1891        /***********************************************************************
1892        * If specified, write header section to window.  If an update operation,
1893        * it is assumed that the number of lines has not changed.
1894        ***********************************************************************/
1895        if (FW->NhLines > 0) DrawSection (FW->wid, FW->hRowT, FW->hRowB, 0,
1896 					 FW->NhLines, FW->hLines, FW->nColS);
1897        /***********************************************************************
1898        * Write fields section to window.  If an update operation, note that
1899        * the number of lines may have changed.
1900        ***********************************************************************/
1901        if (op == UPDATEfw) {
1902 	 if (FW->fUpTo != NULL) cdf_FreeMemory (FW->fUpTo, FatalError);
1903 	 if (FW->fDownTo != NULL) cdf_FreeMemory (FW->fDownTo, FatalError);
1904 	 if (FW->fLeftTo != NULL) cdf_FreeMemory (FW->fLeftTo, FatalError);
1905 	 if (FW->fRightTo != NULL) cdf_FreeMemory (FW->fRightTo, FatalError);
1906        }
1907        if (FW->nFields > 0) {
1908 	 size_t nBytes = FW->nFields * sizeof(int);
1909 	 FW->fUpTo = (int *) cdf_AllocateMemory (nBytes, FatalError);
1910 	 FW->fDownTo = (int *) cdf_AllocateMemory (nBytes, FatalError);
1911 	 FW->fLeftTo = (int *) cdf_AllocateMemory (nBytes, FatalError);
1912 	 FW->fRightTo = (int *) cdf_AllocateMemory (nBytes, FatalError);
1913 	 CalcItemDirections (FW->nFields, FW->NfLines, FW->fLineNs, FW->fCols,
1914 			     FW->fLens, FW->fDownTo, FW->fUpTo, FW->fLeftTo,
1915 			     FW->fRightTo);
1916        }
1917        else {
1918 	 FW->fUpTo = NULL;
1919 	 FW->fDownTo = NULL;
1920 	 FW->fLeftTo = NULL;
1921 	 FW->fRightTo = NULL;
1922        }
1923        if (FW->NfLines > FW->NfRows)
1924 	 FW->fScroll = TRUE;
1925        else
1926 	 FW->fScroll = FALSE;
1927        /***********************************************************************
1928        * Determine row number for current field.  If this is an UPDATEfw
1929        * operation, try to keep the row number the same as it was (or as
1930        * close as possible).
1931        ***********************************************************************/
1932        if (FW->nFields > 0) {
1933 	 if (op == NEWfw)
1934 	   FW->fRowN = MinInt (FW->fRowT + FW->fLineNs[FW->fieldN], FW->fRowB);
1935 	 else {
1936 	   if (FW->fScroll) {
1937 	     int nLinesFromTop = FW->fLineNs[FW->fieldN];
1938 	     int nRowsFromTop = FW->fRowN - FW->fRowT;
1939 	     if (nLinesFromTop < nRowsFromTop)
1940 	       FW->fRowN -= nRowsFromTop - nLinesFromTop;
1941 	     else {
1942 	       int nLinesFromBot = (FW->NfLines-1) - FW->fLineNs[FW->fieldN];
1943 	       int nRowsFromBot = FW->fRowB - FW->fRowN;
1944 	       if (nLinesFromBot < nRowsFromBot)
1945 		 FW->fRowN += nRowsFromBot - nLinesFromBot;
1946 	     }
1947 	   }
1948 	   else
1949 	     FW->fRowN = FW->fRowT + FW->fLineNs[FW->fieldN];
1950 	 }
1951        }
1952        else
1953 	 FW->fRowN = FW->fRowT;
1954        /*********************************************************************
1955        * Draw fields section.
1956        *********************************************************************/
1957        DrawFieldsSection (FW);
1958        /*********************************************************************
1959        * Setup/display initial percentage indicator.
1960        *********************************************************************/
1961        if (FW->fPct) {
1962 	 int lineNt = FWtopRowLineN (FW);
1963 	 if (op == NEWfw) {
1964 	   FW->fPctRowN = FW->fRowB + 1;
1965 	   FW->fPctColN = FW->nColsTotal - 7;
1966 	 }
1967 	 UpdatePctString (FW->wid, FW->NfLines, FW->NfRows, lineNt,
1968 			  FW->fPctRowN, FW->fPctColN);
1969        }
1970        /***********************************************************************
1971        * If specified, write trailer section to window.  If an update
1972        * operation, it is assumed that the number of lines has not changed.
1973        ***********************************************************************/
1974        if (FW->NtLines > 0) DrawSection (FW->wid, FW->tRowT, FW->tRowB, 0,
1975 					 FW->NtLines, FW->tLines, FW->nColS);
1976        /***********************************************************************
1977        * Update screen and place the cursor in the current field.
1978        ***********************************************************************/
1979        end_pasteboard_update ();
1980        FW->charN = 0;
1981        FW->leftCharN = 0;
1982        if (FW->nFields > 0) {
1983 	 set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
1984        }
1985        return TRUE;
1986      }
1987      /*************************************************************************
1988      * Check for a beep request.
1989      *************************************************************************/
1990      case BEEPfw:
1991        ring_bell ();
1992        va_end (ap);
1993        return TRUE;
1994      /*************************************************************************
1995      * Check if menu should be deleted.
1996      *************************************************************************/
1997      case DELETEfw:
1998        if (FW->fUpTo != NULL) cdf_FreeMemory (FW->fUpTo, FatalError);
1999        if (FW->fDownTo != NULL) cdf_FreeMemory (FW->fDownTo, FatalError);
2000        if (FW->fLeftTo != NULL) cdf_FreeMemory (FW->fLeftTo, FatalError);
2001        if (FW->fRightTo != NULL) cdf_FreeMemory (FW->fRightTo, FatalError);
2002        delete_virtual_display (FW->wid);
2003        va_end (ap);
2004        return TRUE;
2005      /*************************************************************************
2006      * Check if menu should be erased (but remain in existence).
2007      *************************************************************************/
2008      case UNDISPLAYfw:
2009        unpaste_virtual_display (FW->wid);
2010        va_end (ap);
2011        return TRUE;
2012      /*************************************************************************
2013      * Check if menu should be redisplayed (but don't wait for input - a call
2014      * with `op = READfw' should be made next).
2015      *************************************************************************/
2016      case REDISPLAYfw:
2017        set_cursor_mode (CURSORon);
2018        paste_virtual_display (FW->wid, FW->ULrow, FW->ULcol);
2019        va_end (ap);
2020        return TRUE;
2021      /*************************************************************************
2022      * Get next input.  The cursor is positioned first in case it had been
2023      * moved.
2024      *************************************************************************/
2025      case READfw: {
2026        int colN;
2027        if (FW->nFields < 1) return FALSE;
2028        set_cursor_mode (CURSORon);
2029        colN = FW->fCols[FW->fieldN] + (FW->charN - FW->leftCharN) + 1;
2030        set_cursor_abs (FW->wid, FW->fRowN, colN);
2031        for (;;) {
2032 	  read_input (
2033 #if defined(CURSESui)
2034 		      FW->wid,
2035 #endif
2036 			       &(FW->key), PASSTHRUri, TRUE);
2037 	  /********************************************************************
2038 	  * Check for an exit key.  If no exit keys were specified, any key
2039 	  * causes an exit.
2040 	  ********************************************************************/
2041 	  if (FW->exitChars[0] == NUL) {
2042 	    va_end (ap);
2043 	    return TRUE;
2044 	  }
2045 	  else {
2046 	    int charN;
2047 	    for (charN = 0; FW->exitChars[charN] != NUL; charN++)
2048 	       if (FW->key == FW->exitChars[charN]) {
2049 		 va_end (ap);
2050 		 return TRUE;
2051 	       }
2052 	  }
2053 	  /********************************************************************
2054 	  * Check for next-screen key.
2055 	  ********************************************************************/
2056 	  if (FW->key == FW->NSkey) {
2057 	    begin_pasteboard_update ();
2058 	    NextScreen (FW->NfRows, FW->NfLines, FW->fLineNs, FW->fDownTo,
2059 			FW->fRowB, FW->fRowT, FW->fScroll, &(FW->fieldN),
2060 			&(FW->fRowN));
2061 	    DrawFieldsSection (FW);
2062 	    if (FW->fPct) UpdatePctString (FW->wid, FW->NfLines, FW->NfRows,
2063 					   FWtopRowLineN(FW), FW->fPctRowN,
2064 					   FW->fPctColN);
2065 	    end_pasteboard_update ();
2066 	    FW->charN = 0;
2067 	    FW->leftCharN = 0;
2068 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2069 	    continue;
2070 	  }
2071 	  /********************************************************************
2072 	  * Check for prev-screen key.
2073 	  ********************************************************************/
2074 	  if (FW->key == FW->PSkey) {
2075 	    begin_pasteboard_update ();
2076 	    PrevScreen (FW->NfRows, FW->fLineNs, FW->fUpTo, FW->fRowT,
2077 			FW->fScroll, &(FW->fRowN), &(FW->fieldN));
2078 	    DrawFieldsSection (FW);
2079 	    if (FW->fPct) UpdatePctString (FW->wid, FW->NfLines, FW->NfRows,
2080 					   FWtopRowLineN(FW), FW->fPctRowN,
2081 					   FW->fPctColN);
2082 	    end_pasteboard_update ();
2083 	    FW->charN = 0;
2084 	    FW->leftCharN = 0;
2085 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2086 	    continue;
2087 	  }
2088 	  /********************************************************************
2089 	  * Check for down field key.
2090 	  ********************************************************************/
2091 	  if (FW->key == FW_DOWN_FIELD || FW->key == FW_DOWN_FIELDx) {
2092 	    begin_pasteboard_update ();
2093 	    DownArrow (FW->fLineNs, FW->fDownTo, FW->fRowT, FW->fRowB,
2094 		       FW->fScroll, &(FW->fRowN), &(FW->fieldN));
2095 	    DrawFieldsSection (FW);
2096 	    if (FW->fPct) UpdatePctString (FW->wid, FW->NfLines, FW->NfRows,
2097 					   FWtopRowLineN(FW), FW->fPctRowN,
2098 					   FW->fPctColN);
2099 	    end_pasteboard_update ();
2100 	    FW->charN = 0;
2101 	    FW->leftCharN = 0;
2102 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2103 	    continue;
2104 	  }
2105 	  /********************************************************************
2106 	  * Check for up field key.
2107 	  ********************************************************************/
2108 	  if (FW->key == FW_UP_FIELD || FW->key == FW_UP_FIELDx) {
2109 	    begin_pasteboard_update ();
2110 	    UpArrow (FW->NfLines, FW->fLineNs, FW->fUpTo, FW->fRowT,
2111 		     FW->fRowB, FW->fScroll, &(FW->fRowN), &(FW->fieldN));
2112 	    DrawFieldsSection (FW);
2113 	    if (FW->fPct) UpdatePctString (FW->wid, FW->NfLines, FW->NfRows,
2114 					   FWtopRowLineN(FW), FW->fPctRowN,
2115 					   FW->fPctColN);
2116 	    end_pasteboard_update ();
2117 	    FW->charN = 0;
2118 	    FW->leftCharN = 0;
2119 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2120 	    continue;
2121 	  }
2122 	  /********************************************************************
2123 	  * Check for left field key.
2124 	  ********************************************************************/
2125 	  if (FW->key == FW_LEFT_FIELD || FW->key == FW_LEFT_FIELDx) {
2126 	    FW->fieldN = FW->fLeftTo[FW->fieldN];
2127 	    FW->charN = 0;
2128 	    FW->leftCharN = 0;
2129 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2130 	    continue;
2131 	  }
2132 	  /********************************************************************
2133 	  * Check for right field key.
2134 	  ********************************************************************/
2135 	  if (FW->key == FW_RIGHT_FIELD || FW->key == FW_RIGHT_FIELDx) {
2136 	    FW->fieldN = FW->fRightTo[FW->fieldN];
2137 	    FW->charN = 0;
2138 	    FW->leftCharN = 0;
2139 	    set_cursor_abs (FW->wid, FW->fRowN, FW->fCols[FW->fieldN] + 1);
2140 	    continue;
2141 	  }
2142 	  /********************************************************************
2143 	  * Check for left character key.
2144 	  ********************************************************************/
2145 	  if (FW->key == FW_LEFT_CHAR || FW->key == FW_LEFT_CHARx) {
2146 	    if (FW->charN > 0) {
2147 	      int colN, len; char *ptr;
2148 	      if (FW->charN == FW->leftCharN) {
2149 		FW->leftCharN--;
2150 		ptr = &(FW->fields[FW->fieldN][FW->leftCharN]);
2151 		len = (int) strlen(ptr);
2152 		put_chars (FW->wid, ptr, MINIMUM(len,FW->fLens[FW->fieldN]),
2153 			   FW->fRowN, FW->fCols[FW->fieldN] + 1, FALSE, NORMAL);
2154 	      }
2155 	      FW->charN--;
2156 	      colN = FW->fCols[FW->fieldN] + (FW->charN - FW->leftCharN);
2157 	      set_cursor_abs (FW->wid, FW->fRowN, colN + 1);
2158 	    }
2159 	    else
2160 	      ring_bell ();
2161 	    continue;
2162 	  }
2163 	  /********************************************************************
2164 	  * Check for right character key.
2165 	  ********************************************************************/
2166 	  if (FW->key == FW_RIGHT_CHAR || FW->key == FW_RIGHT_CHARx) {
2167 	    int fieldLen = (int) strlen(FW->fields[FW->fieldN]);
2168 	    if (FW->charN < fieldLen) {
2169 	      int colN, cursorPos = FW->charN - FW->leftCharN + 1;
2170 	      if (cursorPos == FW->fLens[FW->fieldN]) {
2171 		char *ptr = &(FW->fields[FW->fieldN][FW->leftCharN+1]);
2172 		if (FW->charN == fieldLen - 1) {
2173 		  int count = FW->fLens[FW->fieldN] - 1;
2174 		  put_chars (FW->wid, ptr, count, FW->fRowN,
2175 			     FW->fCols[FW->fieldN] + 1, FALSE, NORMAL);
2176 		  colN = FW->fCols[FW->fieldN] + count;
2177 		  put_chars (FW->wid, " ", 1, FW->fRowN, colN + 1,
2178 			     FALSE, NORMAL);
2179 		}
2180 		else {
2181 		  put_chars (FW->wid, ptr, FW->fLens[FW->fieldN], FW->fRowN,
2182 			     FW->fCols[FW->fieldN] + 1, FALSE, NORMAL);
2183 		}
2184 		FW->leftCharN++;
2185 	      }
2186 	      FW->charN++;
2187 	      colN = FW->fCols[FW->fieldN] + (FW->charN - FW->leftCharN);
2188 	      set_cursor_abs (FW->wid, FW->fRowN, colN + 1);
2189 	    }
2190 	    else
2191 	      ring_bell ();
2192 	    continue;
2193 	  }
2194 	  /********************************************************************
2195 	  * Check for delete key.
2196 	  ********************************************************************/
2197 	  if (FW->key == KB_DELETE) {
2198 	    if (FW->charN > 0) {
2199 	      if (FW->charN == FW->leftCharN) {
2200 		char *ptr = &(FW->fields[FW->fieldN][FW->charN]);
2201 		memmove (ptr - 1, ptr, strlen(ptr) + 1);
2202 		FW->charN--;
2203 		FW->leftCharN--;
2204 	      }
2205 	      else {
2206 		char *ptr;
2207 		ptr = &(FW->fields[FW->fieldN][FW->charN]);
2208 		memmove (ptr - 1, ptr, strlen(ptr) + 1);
2209 		FW->charN--;
2210 		DrawField (FW);
2211 	      }
2212 	    }
2213 	    else
2214 	      ring_bell ();
2215 	    continue;
2216 	  }
2217 	  /********************************************************************
2218 	  * Check for the `refresh' key.
2219 	  ********************************************************************/
2220 	  if (FW->key == FW->refreshChar) {
2221 	    repaint_screen ();
2222 	    continue;
2223 	  }
2224 	  /********************************************************************
2225 	  * Check for the toggle insert/overstrike key.
2226 	  ********************************************************************/
2227 	  if (FW->key == FW->toggleKey) {
2228 	    FW->insert = BOO(FW->insert,FALSE,TRUE);
2229 	    continue;
2230 	  }
2231 	  /********************************************************************
2232 	  * Character to insert/overstrike.
2233 	  ********************************************************************/
2234 	  if (Printable(FW->key)) {
2235 	    if (FW->insert) {
2236 	      int len = (int) strlen(FW->fields[FW->fieldN]);
2237 	      if (len < FW->fMaxs[FW->fieldN]) {
2238 		char *ptr = &(FW->fields[FW->fieldN][FW->charN]);
2239 		int len = (int) strlen(ptr);
2240 		memmove (ptr + 1, ptr, len + 1);
2241 		*ptr = (char) FW->key;
2242 		if (FW->charN - FW->leftCharN == FW->fLens[FW->fieldN] - 1) {
2243 		  FW->leftCharN++;
2244 		}
2245 		FW->charN++;
2246 		DrawField (FW);
2247 	      }
2248 	      else
2249 		ring_bell ();
2250 	    }
2251 	    else {
2252 	      if (FW->charN < FW->fMaxs[FW->fieldN]) {
2253 		int len = (int) strlen(FW->fields[FW->fieldN]);
2254 		char *ptr = &(FW->fields[FW->fieldN][FW->charN]);
2255 		*ptr = (char) FW->key;
2256 		if (FW->charN == len) *(ptr+1) = NUL;
2257 		if (FW->charN - FW->leftCharN == FW->fLens[FW->fieldN] - 1) {
2258 		  FW->leftCharN++;
2259 		}
2260 		FW->charN++;
2261 		DrawField (FW);
2262 	      }
2263 	      else
2264 		ring_bell ();
2265 	    }
2266 	    continue;
2267 	  }
2268 	  /********************************************************************
2269 	  * Illegal key, ring the bell.
2270 	  ********************************************************************/
2271 	  ring_bell ();
2272        }
2273      }
2274    }
2275    va_end (ap);
2276    return FALSE;                                      /* Illegal operation. */
2277 }
2278 
2279 /******************************************************************************
2280 * DrawField.
2281 ******************************************************************************/
2282 
DrawField(FW)2283 static void DrawField (FW)
2284 struct FieldWindowStruct *FW;
2285 {
2286   char *ptr = &(FW->fields[FW->fieldN][FW->leftCharN]);
2287   int i, colN, len = (int) strlen(ptr);
2288   put_chars (FW->wid, ptr, MINIMUM(len,FW->fLens[FW->fieldN]),
2289 	     FW->fRowN, FW->fCols[FW->fieldN] + 1, FALSE, NORMAL);
2290   for (i = len; i < FW->fLens[FW->fieldN]; i++) {
2291      put_chars (FW->wid, " ", 1, FW->fRowN,
2292 		FW->fCols[FW->fieldN] + i + 1, FALSE, NORMAL);
2293   }
2294   colN = FW->fCols[FW->fieldN] + (FW->charN - FW->leftCharN);
2295   set_cursor_abs (FW->wid, FW->fRowN, colN + 1);
2296   return;
2297 }
2298 
2299 /******************************************************************************
2300 * DrawFieldsSection.
2301 ******************************************************************************/
2302 
DrawFieldsSection(FW)2303 static void DrawFieldsSection (FW)
2304 struct FieldWindowStruct *FW; /* Pointer to FieldWindow structure. */
2305 {
2306   size_t len; int i, rowN, lineN, fieldN; char line[SCREEN_WIDTH+1];
2307   for (rowN = FW->fRowT, lineN = FWtopRowLineN(FW);
2308        rowN <= FW->fRowB && lineN < FW->NfLines; rowN++, lineN++) {
2309      strcpyX (line, FW->fLines[lineN], SCREEN_WIDTH);
2310      for (fieldN = 0; fieldN < FW->nFields; fieldN++) {
2311 	if (FW->fLineNs[fieldN] == lineN) {
2312 	  len = (int) strlen(FW->fields[fieldN]);
2313 	  for (i = 0; i < FW->fLens[fieldN]; i++) {
2314 	     if (i < (int) len)
2315 	       line[FW->fCols[fieldN]+i] = FW->fields[fieldN][i];
2316 	     else
2317 	       line[FW->fCols[fieldN]+i] = ' ';
2318 	  }
2319 	}
2320      }
2321      len = (int) strlen(line);
2322      put_chars (FW->wid, line, MINIMUM(FW->nColS,(int)len), rowN, 1,
2323 		FALSE, NORMAL);
2324      if ((int)len < FW->nColS)
2325 	erase_display (FW->wid, rowN, (int)len + 1, rowN, FW->nColS);
2326   }
2327   if (rowN <= FW->fRowB) erase_display (FW->wid, rowN, 1, FW->fRowB, FW->nColS);
2328   return;
2329 }
2330 
2331 /******************************************************************************
2332 * CalcItemDirections.
2333 ******************************************************************************/
2334 
CalcItemDirections(nItems,NiLines,iLineNs,iCols,iLens,iDownTo,iUpTo,iLeftTo,iRightTo)2335 static void CalcItemDirections (nItems, NiLines, iLineNs, iCols, iLens,
2336 				iDownTo, iUpTo, iLeftTo, iRightTo)
2337 int nItems;
2338 int NiLines;
2339 int *iLineNs;
2340 int *iCols;
2341 int *iLens;
2342 int *iDownTo;
2343 int *iUpTo;
2344 int *iLeftTo;
2345 int *iRightTo;
2346 {
2347    int *center, i;
2348    /***************************************************************************
2349    * Allocate and calculate center point for each item.
2350    ***************************************************************************/
2351    center = (int *) cdf_AllocateMemory (nItems * sizeof(int),
2352 				    FatalError);
2353    for (i = 0; i < nItems; i++) {
2354       center[i] = iCols[i] + (iLens[i] / 2);
2355    }
2356    /***************************************************************************
2357    * Calculate directions for each item.
2358    ***************************************************************************/
2359    for (i = 0; i < nItems; i++) {
2360       /************************************************************************
2361       * Calculate down direction.
2362       ************************************************************************/
2363       iDownTo[i] = i;
2364       if (NiLines > 1 && nItems > 1) {
2365 	int toLineN, mostOff, j;
2366 	int fromLineN = iLineNs[i];
2367 	for (j = (i+1) % nItems, toLineN = -1; j != i; j = (j+1) % nItems) {
2368 	   int lineNt = iLineNs[j];
2369 	   if (lineNt != fromLineN)
2370 	     if (toLineN == -1) {
2371 	       iDownTo[i] = j;
2372 	       toLineN = lineNt;
2373 	       mostOff = DIFF(center[i],center[j]);
2374 	     }
2375 	     else
2376 	       if (lineNt == toLineN) {
2377 		 int offBy = DIFF(center[i],center[j]);
2378 		 if (offBy < mostOff) {
2379 		   iDownTo[i] = j;
2380 		   mostOff = offBy;
2381 		 }
2382 	       }
2383 	       else
2384 		 break;         /* No more items on `toLineN'. */
2385 	}
2386       }
2387       /************************************************************************
2388       * Calculate up direction.
2389       ************************************************************************/
2390       iUpTo[i] = i;
2391       if (NiLines > 1 && nItems > 1) {
2392 	int toLineN, mostOff, j;
2393 	int fromLineN = iLineNs[i];
2394 	for (j = (i == 0 ? nItems-1 : i-1), toLineN = -1;
2395 	     j != i; j = (j == 0 ? nItems-1 : j-1)) {
2396 	   int lineNt = iLineNs[j];
2397 	   if (lineNt != fromLineN)
2398 	     if (toLineN == -1) {
2399 	       iUpTo[i] = j;
2400 	       toLineN = lineNt;
2401 	       mostOff = DIFF(center[i],center[j]);
2402 	     }
2403 	     else
2404 	       if (lineNt == toLineN) {
2405 		 int offBy = DIFF(center[i],center[j]);
2406 		 if (offBy < mostOff) {
2407 		   iUpTo[i] = j;
2408 		   mostOff = offBy;
2409 		 }
2410 	       }
2411 	       else
2412 		 break;         /* No more items on `toLineN'. */
2413 	}
2414       }
2415       /************************************************************************
2416       * Calculate left direction.  First check for the nearest item to the
2417       * left.  If none found, check for the farthest item to the right (if
2418       * none found, going left stays at the same item).
2419       ************************************************************************/
2420       iLeftTo[i] = i;
2421       if (nItems > 1) {
2422 	int toLeftLineN = (i > 0 ? iLineNs[i-1] : -1), j;
2423 	if (toLeftLineN == iLineNs[i])
2424 	  iLeftTo[i] = i - 1;
2425 	else
2426 	  for (j = i+1; j < nItems; j++)
2427 	     if (iLineNs[j] == iLineNs[i])
2428 	       iLeftTo[i] = j;
2429 	     else
2430 	       break;
2431       }
2432       /************************************************************************
2433       * Calculate right direction.  First check for the nearest item to the
2434       * right.  If none found, check for the farthest item to the left (if
2435       * none found, going right stays at the same item).
2436       ************************************************************************/
2437       iRightTo[i] = i;
2438       if (nItems > 1) {
2439 	int toRightLineN = (i < nItems-1 ? iLineNs[i+1] : -1), j;
2440 	if (toRightLineN == iLineNs[i])
2441 	  iRightTo[i] = i + 1;
2442 	else
2443 	  for (j = i-1; j >= 0; j--)
2444 	     if (iLineNs[j] == iLineNs[i])
2445 	       iRightTo[i] = j;
2446 	     else
2447 	       break;
2448       }
2449    }
2450    cdf_FreeMemory (center, FatalError);
2451    return;
2452 }
2453 
2454 /******************************************************************************
2455 * UpdatePctString.
2456 ******************************************************************************/
2457 
UpdatePctString(wid,nLines,nRows,topRowLineN,rowN,colN)2458 static void UpdatePctString (wid, nLines, nRows, topRowLineN, rowN, colN)
2459 WINDOWid wid;
2460 int nLines;
2461 int nRows;
2462 int topRowLineN;
2463 int rowN;
2464 int colN;
2465 {
2466   char pct[MAX_PCT_LEN+1];
2467   if (nLines <= nRows)
2468     strcpyX (pct, " All ", MAX_PCT_LEN);
2469   else
2470     if (topRowLineN == 0)
2471       strcpyX (pct, " Top ", MAX_PCT_LEN);
2472     else
2473       if (topRowLineN == nLines - nRows)
2474 	strcpyX (pct, " End ", MAX_PCT_LEN);
2475       else {
2476 	sprintf (pct, "%3d%% ", (int)
2477 		 ((100.0 * (((float) topRowLineN) / (nLines - nRows))) + 0.5));
2478       }
2479   put_chars (wid, pct, (int) strlen(pct), rowN, colN, FALSE, NORMAL);
2480   return;
2481 }
2482 
2483 /******************************************************************************
2484 * DrawSection.
2485 ******************************************************************************/
2486 
DrawSection(wid,rowT,rowB,topRowLineN,nLines,lineS,nColS)2487 static void DrawSection (wid, rowT, rowB, topRowLineN, nLines, lineS, nColS)
2488 WINDOWid wid;
2489 int rowT;               /* Top row number. */
2490 int rowB;               /* Bottom row number. */
2491 int topRowLineN;        /* Line number to be displayed in top row. */
2492 int nLines;             /* Number of lines (may be greater than the number of
2493 			   available rows). */
2494 char **lineS;           /* Lines to be displayed.  Capital `S' because of the
2495 			   IBM RS6000. */
2496 int nColS;              /* Number of available columns (first character of a
2497 			   line is displayed in column one [1]). */
2498 {
2499   size_t len;
2500   int rowN, lineN;
2501   for (rowN = rowT, lineN = topRowLineN;
2502        rowN <= rowB && lineN < nLines; rowN++, lineN++) {
2503      len = strlen(lineS[lineN]);
2504      put_chars (wid, lineS[lineN], MINIMUM(nColS,(int)len), rowN, 1,
2505 		FALSE, NORMAL);
2506      if ((int)len < nColS)
2507 	erase_display (wid, rowN, (int)len + 1, rowN, nColS);
2508   }
2509   if (rowN <= rowB) erase_display (wid, rowN, 1, rowB, nColS);
2510   return;
2511 }
2512 
2513 /******************************************************************************
2514 * NextScreen.
2515 ******************************************************************************/
2516 
NextScreen(NiRows,NiLines,iLineNs,iDownTo,iRowB,iRowT,iScroll,itemN,iRowN)2517 static void NextScreen (NiRows, NiLines, iLineNs, iDownTo, iRowB, iRowT,
2518 			iScroll, itemN, iRowN)
2519 int NiRows;
2520 int NiLines;
2521 int *iLineNs;
2522 int *iDownTo;
2523 int iRowB;
2524 int iRowT;
2525 Logical iScroll;
2526 int *itemN;
2527 int *iRowN;
2528 {
2529   int oldLineN = iLineNs[*itemN];
2530   int maxLineN = MinInt (oldLineN + NiRows, NiLines - 1);
2531   for (;;) {
2532      int itemNt = iDownTo[*itemN];
2533      int lineNt = iLineNs[itemNt];
2534      if (lineNt <= maxLineN && lineNt > oldLineN)
2535        *itemN = itemNt;
2536      else
2537        break;
2538   }
2539   if (iScroll) {
2540     int linesToEnd = (NiLines - 1) - iLineNs[*itemN];
2541     if (*iRowN + linesToEnd < iRowB) *iRowN = iRowB - linesToEnd;
2542   }
2543   else
2544     *iRowN = iRowT + iLineNs[*itemN];
2545   return;
2546 }
2547 
2548 /******************************************************************************
2549 * PrevScreen.
2550 ******************************************************************************/
2551 
PrevScreen(NiRows,iLineNs,iUpTo,iRowT,iScroll,iRowN,itemN)2552 static void PrevScreen (NiRows, iLineNs, iUpTo, iRowT, iScroll, iRowN, itemN)
2553 int NiRows;
2554 int *iLineNs;
2555 int *iUpTo;
2556 int iRowT;
2557 Logical iScroll;
2558 int *iRowN;
2559 int *itemN;
2560 {
2561   int oldLineN = iLineNs[*itemN];
2562   int minLineN = MaxInt (oldLineN - NiRows, 0);
2563   for (;;) {
2564      int itemNt = iUpTo[*itemN];
2565      int lineNt = iLineNs[itemNt];
2566      if (lineNt >= minLineN && lineNt < oldLineN)
2567        *itemN = itemNt;
2568      else
2569        break;
2570   }
2571   if (iScroll) {
2572     int linesToBeg = iLineNs[*itemN];
2573     if (*iRowN - linesToBeg > iRowT) *iRowN = iRowT + linesToBeg;
2574   }
2575   else
2576     *iRowN = iRowT + iLineNs[*itemN];
2577   return;
2578 }
2579 
2580 /******************************************************************************
2581 * DownArrow.
2582 ******************************************************************************/
2583 
DownArrow(iLineNs,iDownTo,iRowT,iRowB,iScroll,iRowN,itemN)2584 static void DownArrow (iLineNs, iDownTo, iRowT, iRowB, iScroll, iRowN, itemN)
2585 int *iLineNs;
2586 int *iDownTo;
2587 int iRowT;
2588 int iRowB;
2589 Logical iScroll;
2590 int *iRowN;
2591 int *itemN;
2592 {
2593   int oldLineN = iLineNs[*itemN];
2594   *itemN = iDownTo[*itemN];
2595   if (iScroll) {
2596     int nLinesDown = iLineNs[*itemN] - oldLineN;
2597     if (nLinesDown < 0)
2598       *iRowN = iRowT + iLineNs[*itemN];
2599     else
2600       *iRowN = MINIMUM (*iRowN + nLinesDown, iRowB);
2601   }
2602   else
2603     *iRowN = iRowT + iLineNs[*itemN];
2604   return;
2605 }
2606 
2607 /******************************************************************************
2608 * UpArrow.
2609 ******************************************************************************/
2610 
UpArrow(NiLines,iLineNs,iUpTo,iRowT,iRowB,iScroll,iRowN,itemN)2611 static void UpArrow (NiLines, iLineNs, iUpTo, iRowT, iRowB, iScroll, iRowN,
2612 		     itemN)
2613 int NiLines;
2614 int *iLineNs;
2615 int *iUpTo;
2616 int iRowT;
2617 int iRowB;
2618 Logical iScroll;
2619 int *iRowN;
2620 int *itemN;
2621 {
2622   int oldLineN = iLineNs[*itemN];
2623   *itemN = iUpTo[*itemN];
2624   if (iScroll) {
2625     int nLinesUp = oldLineN - iLineNs[*itemN];
2626     if (nLinesUp < 0) {
2627       int nLinesFromBot = (NiLines - 1) - iLineNs[*itemN];
2628       *iRowN = iRowB - nLinesFromBot;
2629     }
2630     else
2631       *iRowN = MaxInt (*iRowN - nLinesUp, iRowT);
2632   }
2633   else
2634     *iRowN = iRowT + iLineNs[*itemN];
2635   return;
2636 }
2637 
2638 /******************************************************************************
2639 * IWtopRowLineN.
2640 ******************************************************************************/
2641 
IWtopRowLineN(IW)2642 static int IWtopRowLineN (IW)
2643 struct ItemWindowStruct *IW;
2644 {
2645   return BOO(IW->nItems > 0,
2646 	     IW->iLineNs[IW->itemN] - (IW->iRowN - IW->iRowT), 0);
2647 }
2648 
2649 /******************************************************************************
2650 * FWtopRowLineN.
2651 ******************************************************************************/
2652 
FWtopRowLineN(FW)2653 static int FWtopRowLineN (FW)
2654 struct FieldWindowStruct *FW;
2655 {
2656   return BOO(FW->nFields > 0,
2657 	     FW->fLineNs[FW->fieldN] - (FW->fRowN - FW->fRowT), 0);
2658 }
2659 
2660 /******************************************************************************
2661 * AllocIW.
2662 ******************************************************************************/
2663 
AllocIW(IW,nItems,NiLines,iLineNchars,fatalFnc)2664 void AllocIW (IW, nItems, NiLines, iLineNchars, fatalFnc)
2665 struct ItemWindowStruct *IW;
2666 int nItems;
2667 int NiLines;
2668 int iLineNchars;
2669 void (*fatalFnc) PROTOARGs((char *msg));
2670 {
2671    IW->nItems = nItems;
2672    if (IW->nItems > 0) {
2673      IW->iLineNs = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
2674 					   fatalFnc);
2675      IW->iCols = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
2676 					 fatalFnc);
2677      IW->iLens = (int *) cdf_AllocateMemory (IW->nItems * sizeof(int),
2678 					 fatalFnc);
2679    }
2680    IW->NiLines = NiLines;
2681    if (IW->NiLines > 0) {
2682      int lineN, i;
2683      IW->iLines = (char **) cdf_AllocateMemory (IW->NiLines * sizeof(char *),
2684 					    fatalFnc);
2685      for (lineN = 0; lineN < IW->NiLines; lineN++) {
2686 	IW->iLines[lineN] = (char *) cdf_AllocateMemory (iLineNchars+1,
2687 						     fatalFnc);
2688 	for (i = 0; i < iLineNchars; i++) IW->iLines[lineN][i] = ' ';
2689 	IW->iLines[lineN][iLineNchars] = NUL;
2690      }
2691    }
2692    return;
2693 }
2694 
2695 /******************************************************************************
2696 * AllocFW.
2697 ******************************************************************************/
2698 
AllocFW(FW,nFields,NfLines,fLineNchars,fatalFnc)2699 void AllocFW (FW, nFields, NfLines, fLineNchars, fatalFnc)
2700 struct FieldWindowStruct *FW;
2701 int nFields;
2702 int NfLines;
2703 int fLineNchars;
2704 void (*fatalFnc) PROTOARGs((char *msg));
2705 {
2706    FW->nFields = nFields;
2707    if (FW->nFields > 0) {
2708      FW->fLineNs = (int *) cdf_AllocateMemory (FW->nFields * sizeof(int),
2709 					   fatalFnc);
2710      FW->fCols = (int *) cdf_AllocateMemory (FW->nFields * sizeof(int),
2711 					 fatalFnc);
2712      FW->fLens = (int *) cdf_AllocateMemory (FW->nFields * sizeof(int),
2713 					 fatalFnc);
2714    }
2715    FW->NfLines = NfLines;
2716    if (FW->NfLines > 0) {
2717      int lineN, i;
2718      FW->fLines = (char **) cdf_AllocateMemory (FW->NfLines * sizeof(char *),
2719 					    fatalFnc);
2720      for (lineN = 0; lineN < FW->NfLines; lineN++) {
2721 	FW->fLines[lineN] = (char *) cdf_AllocateMemory (fLineNchars+1,
2722 						     fatalFnc);
2723 	for (i = 0; i < fLineNchars; i++) FW->fLines[lineN][i] = ' ';
2724 	FW->fLines[lineN][fLineNchars] = NUL;
2725      }
2726    }
2727    return;
2728 }
2729 
2730 /******************************************************************************
2731 * FreeIW.
2732 ******************************************************************************/
2733 
FreeIW(IW,fatalFnc)2734 void FreeIW (IW, fatalFnc)
2735 struct ItemWindowStruct *IW;
2736 void (*fatalFnc) PROTOARGs((char *msg));
2737 {
2738    if (IW->NiLines > 0) {
2739      int lineN;
2740      for (lineN = IW->NiLines - 1; lineN >= 0; lineN--) {
2741 	cdf_FreeMemory (IW->iLines[lineN], fatalFnc);
2742      }
2743      cdf_FreeMemory (IW->iLines, fatalFnc);
2744    }
2745    if (IW->nItems > 0) {
2746      cdf_FreeMemory (IW->iLens, fatalFnc);
2747      cdf_FreeMemory (IW->iCols, fatalFnc);
2748      cdf_FreeMemory (IW->iLineNs, fatalFnc);
2749    }
2750    IW->NiLines = 0;
2751    IW->nItems = 0;
2752    IW->iLines = NULL;
2753    IW->iLineNs = NULL;
2754    IW->iCols = NULL;
2755    IW->iLens = NULL;
2756    return;
2757 }
2758 
2759 /******************************************************************************
2760 * FreeFW.
2761 ******************************************************************************/
2762 
FreeFW(FW,fatalFnc)2763 void FreeFW (FW, fatalFnc)
2764 struct FieldWindowStruct *FW;
2765 void (*fatalFnc) PROTOARGs((char *msg));
2766 {
2767    if (FW->NfLines > 0) {
2768      int lineN;
2769      for (lineN = FW->NfLines - 1; lineN >= 0; lineN--) {
2770 	cdf_FreeMemory (FW->fLines[lineN], fatalFnc);
2771      }
2772      cdf_FreeMemory (FW->fLines, fatalFnc);
2773    }
2774    if (FW->nFields > 0) {
2775      cdf_FreeMemory (FW->fLens, fatalFnc);
2776      cdf_FreeMemory (FW->fCols, fatalFnc);
2777      cdf_FreeMemory (FW->fLineNs, fatalFnc);
2778    }
2779    FW->NfLines = 0;
2780    FW->nFields = 0;
2781    FW->fLines = NULL;
2782    FW->fLineNs = NULL;
2783    FW->fCols = NULL;
2784    FW->fLens = NULL;
2785    return;
2786 }
2787 
2788 
2789 /******************************************************************************
2790 * OnlineHelpWindow.
2791 ******************************************************************************/
2792 
OnlineHelpWindow(ilhFile,helpId)2793 Logical OnlineHelpWindow (ilhFile, helpId)
2794 char *ilhFile;
2795 int helpId;
2796 {
2797   AOSs1A (header, BLANKs78)
2798   AOSs1B (trailer,
2799 	  "Exit: ________  NextScreen: ________  PrevScreen: ________")
2800   static char errorLines[] = "Online help not available.\n";
2801   static int exitChars[] = { EXITkey_FSI, NUL };
2802   static char label[] = { BLANKs78 };
2803   static Logical first = TRUE;
2804   static struct EditWindowStruct EW = {
2805     label, 0, 0, SCREEN_WIDTH, 1, header, NULL, 18, 1, trailer, TRUE, TRUE,
2806     exitChars, REFRESHkey_FSI, NUL, NUL, NUL, NUL, NSkey_FSI, PSkey_FSI, NUL,
2807     NUL, NUL
2808   };
2809   /****************************************************************************
2810   * Encode label and key definitions the first time.
2811   ****************************************************************************/
2812   if (first) {
2813     sprintf (EW.label, " %s Online Help ", pgmName);
2814     EncodeKeyDefinitions (1, EW.tLines, EXITkey_FSI, NSkey_FSI, PSkey_FSI);
2815     first = FALSE;
2816   }
2817   /****************************************************************************
2818   * Load online help.
2819   ****************************************************************************/
2820   if (!LoadOnlineHelp(ilhFile,helpId,EW.hLines[0],&EW.eText)) {
2821     cdf_FreeMemory (EW.eText, FatalError);
2822     strcpyX (EW.hLines[0], "Error!", 0);
2823     EW.eText = errorLines;
2824     EditWindow (NEWew, &EW, TRUE);
2825     EditWindow (READew, &EW);
2826     EditWindow (DELETEew, &EW);
2827     return FALSE;
2828   }
2829   /****************************************************************************
2830   * Display help window/wait for exit key.
2831   ****************************************************************************/
2832   EditWindow (NEWew, &EW, TRUE);
2833   EditWindow (READew, &EW);
2834   EditWindow (DELETEew, &EW);
2835   /****************************************************************************
2836   * Cleanup and return.
2837   ****************************************************************************/
2838   cdf_FreeMemory (EW.eText, FatalError);
2839   return TRUE;
2840 }
2841 
2842 /******************************************************************************
2843 * LoadOnlineHelp.
2844 *     You'll notice that the buffers used to hold lines of online help are
2845 * allocated as (SCREEN_WIDTH-2)+1+1.  This is for the number of characters,
2846 * the newline (as returned by `fgets'), and the terminating NUL character.
2847 * When `fgets' is called, the number of characters to read is specified as
2848 * (SCREEN_WIDTH-2)+1+1.  This is for the actual characters plus the newline
2849 * plus one more since `fgets' subtracts one from this value and uses that as
2850 * the maximum number of characters to read (which includes the newline).
2851 ******************************************************************************/
2852 
LoadOnlineHelp(ilhFile,helpId,header,eText)2853 Logical LoadOnlineHelp (ilhFile, helpId, header, eText)
2854 char *ilhFile;
2855 int helpId;
2856 char *header;
2857 char **eText;
2858 {
2859   FILE *fp; int i; size_t length;
2860   char beginStr[15+1], line[(SCREEN_WIDTH-2)+1+1], helpIdStr[15+1];
2861   enum { ITEMw, PROMPTw, EDITw } windowType;
2862   int nBlanks;
2863   int nestLevel = 0;    /* Depth into `#ifos's. */
2864   int osMask = 0;       /* When 0, display line.  Note that bit 0 is not used
2865 			   (eg. nesting depth of 1 uses bit 1, etc.). */
2866 #if defined(vms)
2867   static char thisOS[] = "vms";
2868 #endif
2869 #if (defined(unix) && !defined(__CYGWIN__) && !defined(__MINGW32__)) || \
2870     defined(posixSHELL)
2871   static char thisOS[] = "unix";
2872 #endif
2873 #if defined(dos) || defined(__CYGWIN__) || defined(__MINGW32__)
2874   static char thisOS[] = "dos";
2875 #endif
2876 #if defined(mac)
2877   static char thisOS[] = "mac";
2878 #endif
2879 #if defined(win32)
2880   static char thisOS[] = "win";
2881 #endif
2882   /****************************************************************************
2883   * Initialize.
2884   ****************************************************************************/
2885   *eText = cdf_AllocateMemory ((size_t) 1, FatalError);
2886   MakeNUL (*eText);
2887   /****************************************************************************
2888   * Open help file.
2889   ****************************************************************************/
2890   fp = OnlineHelpFP (ilhFile, NULL);
2891   if (fp == NULL) return FALSE;
2892   /****************************************************************************
2893   * Read through help file looking for proper help section.
2894   ****************************************************************************/
2895   sprintf (beginStr, "#section %d", helpId);
2896   while (fgets(line,(SCREEN_WIDTH-2)+1+1,fp) != NULL) {
2897     /**************************************************************************
2898     * Strip trailing newline character and check to see if the help section
2899     * has been found.
2900     **************************************************************************/
2901     line[strlen(line)-1] = NUL;
2902     if (!strcmp(line,beginStr)) {
2903       /************************************************************************
2904       * Determine window type.
2905       ************************************************************************/
2906       if (fgets(line,(SCREEN_WIDTH-2)+1+1,fp) == NULL) {
2907 	fclose (fp);
2908 	return FALSE;
2909       }
2910       line[strlen(line)-1] = NUL;
2911       if (!strcmp(line,"#item"))
2912 	windowType = ITEMw;
2913       else
2914 	if (!strcmp(line,"#prompt"))
2915 	  windowType = PROMPTw;
2916 	else
2917 	  if (!strcmp(line,"#edit"))
2918 	    windowType = EDITw;
2919 	  else {
2920 	    fclose (fp);
2921 	    return FALSE;
2922 	  }
2923       /************************************************************************
2924       * Build header.
2925       ************************************************************************/
2926       if (fgets(line,(SCREEN_WIDTH-2)+1+1,fp) == NULL) {
2927 	fclose (fp);
2928 	return FALSE;
2929       }
2930       line[strlen(line)-1] = NUL;
2931       if (strncmp(line,"#title ",7) != 0) {
2932 	fclose (fp);
2933 	return FALSE;
2934       }
2935       strcpyX (header, "Help for ", 0);
2936       strcatX (header, &line[7], 0);
2937       sprintf (helpIdStr, "[%d%c]", helpId,
2938 	       (windowType == ITEMw ? 'i' :
2939 		(windowType == PROMPTw ? 'p' :
2940 		 (windowType == EDITw ? 'e' : '?'))));
2941       nBlanks = (SCREEN_WIDTH-2) - strlen(header) - strlen(helpIdStr);
2942       CatNcharacters (header, nBlanks, (int) ' ');
2943       strcatX (header, helpIdStr, 0);
2944       /************************************************************************
2945       * Read and save lines until "#endsection" is found.
2946       ************************************************************************/
2947       while (fgets(line,(SCREEN_WIDTH-2)+1+1,fp) != NULL) {
2948 	line[strlen(line)-1] = NUL;
2949 	/**********************************************************************
2950 	* Check if at end of this section of help.  If so, delete trailing
2951 	* newline characters, encode key definitions based on window type,
2952 	* close help file, and return.
2953 	**********************************************************************/
2954 	if (!strcmp(line,"#endsection")) {
2955 	  if (nestLevel != 0) {
2956 	    fclose (fp);
2957 	    return FALSE;
2958 	  }
2959 	  length = strlen (*eText);
2960 	  if (length > 0) {
2961 	    for (i = length - 1; i >= 0; i--) {
2962 	       if ((*eText)[i] != Nl) break;
2963 	       (*eText)[i] = NUL;
2964 	    }
2965 	  }
2966 	  switch (windowType) {
2967 	    case ITEMw:
2968 	      EncodeKeyDefinitions (1, eText, NSkey_FSI, PSkey_FSI);
2969 	      break;
2970 	    case PROMPTw:
2971 	      EncodeKeyDefinitions (1, eText, SOLkey_FSI, EOLkey_FSI,
2972 				    INSERTorOVERkey_FSI);
2973 	      break;
2974 	    case EDITw:
2975 	      EncodeKeyDefinitions (1, eText, NSkey_FSI, PSkey_FSI);
2976 	      break;
2977 	  }
2978 	  fclose (fp);
2979 	  return TRUE;
2980 	}
2981 	/**********************************************************************
2982 	* Not at end yet.  Check if an operating system directive.  If not,
2983 	* include the line if all of the bits in the operating system mask
2984 	* are clear.
2985 	**********************************************************************/
2986 	if (line[0] == '#') {
2987 	  /********************************************************************
2988 	  * Check for an `#ifos' directive.  If this operating system is not
2989 	  * specified, then set the bit in the operating system mask for this
2990 	  * nesting level.
2991 	  ********************************************************************/
2992 	  if (!strncmp(line,"#ifos",5)) {
2993 	    nestLevel++;
2994 	    if (strstr(line,thisOS) == NULL) SETBIT (osMask, nestLevel);
2995 	    continue;
2996 	  }
2997 	  /********************************************************************
2998 	  * Check for an `#else' directive.  Simply flip the bit in the
2999 	  * operating system mask for this nesting level.
3000 	  ********************************************************************/
3001 	  if (!strcmp(line,"#else")) {
3002 	    if (nestLevel < 1) {
3003 	      fclose (fp);
3004 	      return FALSE;
3005 	    }
3006 	    FLPBIT (osMask, nestLevel);
3007 	    continue;
3008 	  }
3009 	  /********************************************************************
3010 	  * Check for an `#endos' directive.  Clear the bit in the operating
3011 	  * system mask for this nesting level and decrement the nesting level.
3012 	  ********************************************************************/
3013 	  if (!strcmp(line,"#endos")) {
3014 	    if (nestLevel < 1) {
3015 	      fclose (fp);
3016 	      return FALSE;
3017 	    }
3018 	    CLRBIT (osMask, nestLevel);
3019 	    nestLevel--;
3020 	    continue;
3021 	  }
3022 	  /********************************************************************
3023 	  * An unknown directive has been encountered.
3024 	  ********************************************************************/
3025 	  fclose (fp);
3026 	  return FALSE;
3027 	}
3028 	else {
3029 	  if (osMask == 0) {
3030 	    size_t length, newLength;
3031 #if defined(dos)
3032 	    int tabCount, i;
3033 #endif
3034 	    length = strlen (line);
3035 #if defined(dos)
3036 	    for (tabCount = 0, i = 0; i < length; i++) {
3037 	       if (line[i] == Ht) tabCount++;
3038 	    }
3039 	    length += (7 * tabCount);
3040 #endif
3041 	    newLength = strlen(*eText) + (length + 1) + 1;
3042 	    *eText = cdf_ReallocateMemory (*eText, newLength,
3043 				       FatalError);
3044 #if defined(dos)
3045 	    for (i = 0; i < tabCount; i++) {
3046 	       strcatX (*eText, "        ", 0);
3047 	    }
3048 	    strcatX (*eText, &line[i], 0);
3049 #else
3050 	    strcatX (*eText, line, 0);
3051 #endif
3052 	    strcatX (*eText, "\n", 0);
3053 	  }
3054 	}
3055       }
3056       /************************************************************************
3057       * `#endsection' not found - error return.
3058       ************************************************************************/
3059       fclose (fp);
3060       return FALSE;
3061     }
3062   }
3063   /****************************************************************************
3064   * `#section x' not found - error return.
3065   ****************************************************************************/
3066   fclose (fp);
3067   return FALSE;
3068 }
3069 
3070 /******************************************************************************
3071 * InfoWindow.
3072 ******************************************************************************/
3073 
InfoWindow(message1,message2,message3,center,beep,wait)3074 void InfoWindow (message1, message2, message3, center, beep, wait)
3075 char *message1;         /* This message line must exist. */
3076 char *message2;         /* This message line is optional (NULL if absent). */
3077 char *message3;         /* This message line is optional (NULL if absent).
3078 			   If `message2' is NULL, this must also be NULL. */
3079 Logical center;         /* TRUE if window should be in center of screen. */
3080 Logical beep;           /* TRUE if window should beep after being displayed. */
3081 int wait;               /* 0: Read a key before deleting window.
3082 			   >0: Wait `wait' seconds and then delete window. */
3083 {
3084    static int exitChars[] = { NUL };
3085    static char eText[INFOtextMAX+1];
3086    static char ackLabel[] = " Enter any key to acknowledge. ";
3087    static struct EditWindowStruct EW = {
3088      NULL, 0, 0, 0, 0, NULL, eText, 0, 0, NULL, FALSE, TRUE, exitChars,
3089      REFRESHkey_FSI, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL, NUL
3090    };
3091    EW.label = BOO(wait > 0,NULL,ackLabel);
3092    if (center) {
3093      EW.nColsTotal = BOO(EW.label == NULL,0,(int)(strlen(EW.label) + 4));
3094      EW.nColsTotal = MaxInt (EW.nColsTotal, (int) (strlen(message1) + 2));
3095      strcpyX (EW.eText, message1, INFOtextMAX);
3096      strcatX (EW.eText, "\n", INFOtextMAX);
3097      EW.ULrow = (SCREEN_HEIGHT - 3) / 2;
3098      EW.NeRows = 1;
3099      if (message2 != NULL) {
3100        EW.nColsTotal = MaxInt (EW.nColsTotal, (int) (strlen(message2) + 2));
3101        strcatX (EW.eText, message2, INFOtextMAX);
3102        strcatX (EW.eText, "\n", INFOtextMAX);
3103        EW.ULrow = (SCREEN_HEIGHT - 4) / 2;
3104        EW.NeRows = 2;
3105      }
3106      if (message3 != NULL) {
3107        EW.nColsTotal = MaxInt (EW.nColsTotal, (int) (strlen(message3) + 2));
3108        strcatX (EW.eText, message3, INFOtextMAX);
3109        strcatX (EW.eText, "\n", INFOtextMAX);
3110        EW.ULrow = (SCREEN_HEIGHT - 5) / 2;
3111        EW.NeRows = 3;
3112      }
3113      EW.ULcol = (SCREEN_WIDTH - EW.nColsTotal) / 2;
3114    }
3115    else {
3116      strcpyX (EW.eText, message1, INFOtextMAX);
3117      strcatX (EW.eText, "\n", INFOtextMAX);
3118      EW.ULrow = SCREEN_HEIGHT - 3;
3119      EW.NeRows = 1;
3120      if (message2 != NULL) {
3121        strcatX (EW.eText, message2, INFOtextMAX);
3122        strcatX (EW.eText, "\n", INFOtextMAX);
3123        EW.ULrow = SCREEN_HEIGHT - 4;
3124        EW.NeRows = 2;
3125      }
3126      if (message3 != NULL) {
3127        strcatX (EW.eText, message3, INFOtextMAX);
3128        strcatX (EW.eText, "\n", INFOtextMAX);
3129        EW.ULrow = SCREEN_HEIGHT - 5;
3130        EW.NeRows = 3;
3131      }
3132      EW.nColsTotal = SCREEN_WIDTH;
3133      EW.ULcol = 0;
3134    }
3135    EditWindow (NEWew, &EW, TRUE);
3136    if (beep) EditWindow (BEEPew, &EW);
3137    if (wait > 0)
3138      zzzzz ((double) wait);
3139    else
3140      EditWindow (READew, &EW);
3141    EditWindow (DELETEew, &EW);
3142    return;
3143 }
3144