1 #include <cdk_int.h>
2 
3 /*
4  * $Author: tom $
5  * $Date: 2016/11/20 18:56:05 $
6  * $Revision: 1.105 $
7  */
8 
9 #define TITLELINES 1
10 
11 /*
12  * Declare file local prototypes.
13  */
14 static void cleanUpMenu (CDKMENU *menu);
15 
16 DeclareCDKObjects (MENU, Menu, setCdk, Int);
17 
18 /*
19  * This creates a new menu widget.
20  */
newCDKMenu(CDKSCREEN * cdkscreen,const char * menulist[MAX_MENU_ITEMS][MAX_SUB_ITEMS],int menuItems,int * subsize,int * menuLocation,int menuPos,chtype titleAttr,chtype subtitleAttr)21 CDKMENU *newCDKMenu (CDKSCREEN *cdkscreen,
22 		     const char *menulist[MAX_MENU_ITEMS][MAX_SUB_ITEMS],
23 		     int menuItems,
24 		     int *subsize,
25 		     int *menuLocation,
26 		     int menuPos,
27 		     chtype titleAttr,
28 		     chtype subtitleAttr)
29 {
30    /* *INDENT-EQLS* */
31    CDKMENU *menu        = 0;
32    int rightcount;
33    int rightloc         = getmaxx (cdkscreen->window);
34    int leftloc          = 0;
35    int x, y, junk;
36    int xpos             = getbegx (cdkscreen->window);
37    int ypos             = getbegy (cdkscreen->window);
38    int ymax             = getmaxy (cdkscreen->window);
39 
40    if ((menu = newCDKObject (CDKMENU, &my_funcs)) == 0)
41         return (0);
42 
43    /* *INDENT-EQLS* Start making a copy of the information. */
44    ScreenOf (menu)              = cdkscreen;
45    ObjOf (menu)->box            = FALSE;
46    ObjOf (menu)->acceptsFocus   = FALSE;
47    rightcount                   = menuItems - 1;
48    menu->parent                 = cdkscreen->window;
49    menu->menuItems              = menuItems;
50    menu->titleAttr              = titleAttr;
51    menu->subtitleAttr           = subtitleAttr;
52    menu->currentTitle           = 0;
53    menu->currentSubtitle        = 0;
54    menu->lastSelection          = -1;
55    menu->menuPos                = menuPos;
56    initExitType (menu);
57 
58    /* Create the pull down menus. */
59    for (x = 0; x < menuItems; x++)
60    {
61       /* *INDENT-EQLS* */
62       int x1   = (menuLocation[x] == LEFT) ? x : rightcount--;
63       int x2;
64       int y1   = (menuPos == BOTTOM) ? (ymax - 1) : 0;
65       int y2   = (menuPos == BOTTOM) ? (ymax - subsize[x] - 2) : TITLELINES;
66       int high = subsize[x] + TITLELINES;
67       int max  = -1;
68 
69       /*
70        * Limit the menu height to fit on the screen.
71        */
72       if (high + y2 > ymax)
73       {
74 	 high = ymax - TITLELINES;
75       }
76 
77       max = -1;
78       for (y = TITLELINES; y < subsize[x]; y++)
79       {
80 	 int y0 = y - TITLELINES;
81 
82 	 menu->sublist[x1][y0] = char2Chtype (menulist[x][y],
83 					      &menu->sublistLen[x1][y0],
84 					      &junk);
85 	 max = MAXIMUM (max, menu->sublistLen[x1][y0]);
86       }
87 
88       if (menuLocation[x] == LEFT)
89       {
90 	 x2 = leftloc;
91       }
92       else
93       {
94 	 x2 = (rightloc -= max + 2);
95       }
96       /* *INDENT-EQLS* */
97       menu->title[x1]    = char2Chtype (menulist[x][0], &menu->titleLen[x1], &junk);
98       menu->subsize[x1]  = subsize[x] - TITLELINES;
99       menu->titleWin[x1] = subwin (cdkscreen->window,
100 				   TITLELINES,
101 				   menu->titleLen[x1] + 2,
102 				   ypos               + y1,
103 				   xpos               + x2);
104       menu->pullWin[x1]  = subwin (cdkscreen->window,
105 				   high,
106 				   max                + 2,
107 				   ypos               + y2,
108 				   xpos               + x2);
109       if (menu->titleWin[x1] == 0 || menu->pullWin[x1] == 0)
110       {
111 	 destroyCDKMenu (menu);
112 	 return (0);
113       }
114 
115       leftloc += menu->titleLen[x] + 1;
116 
117       keypad (menu->titleWin[x1], TRUE);
118       keypad (menu->pullWin[x1], TRUE);
119    }
120    ObjOf (menu)->inputWindow = menu->titleWin[menu->currentTitle];
121 
122    /* Register this baby. */
123    registerCDKObject (cdkscreen, vMENU, menu);
124 
125    /* Return the menu object. */
126    return (menu);
127 }
128 
129 /*
130  * This activates the CDK Menu.
131  */
activateCDKMenu(CDKMENU * menu,chtype * actions)132 int activateCDKMenu (CDKMENU *menu, chtype *actions)
133 {
134    chtype input;
135    boolean functionKey;
136    int ret;
137 
138    /* Draw in the screen. */
139    refreshCDKScreen (ScreenOf (menu));
140 
141    /* Display the menu titles. */
142    drawCDKMenu (menu, ObjOf (menu)->box);
143 
144    /* Highlight the current title and window. */
145    drawCDKMenuSubwin (menu);
146 
147    /* If the input string is null, this is an interactive activate. */
148    if (actions == 0)
149    {
150       ObjOf (menu)->inputWindow = menu->titleWin[menu->currentTitle];
151 
152       /* Start taking input from the keyboard. */
153       for (;;)
154       {
155 	 input = (chtype)getchCDKObject (ObjOf (menu), &functionKey);
156 
157 	 /* Inject the character into the widget. */
158 	 ret = injectCDKMenu (menu, input);
159 	 if (menu->exitType != vEARLY_EXIT)
160 	 {
161 	    return ret;
162 	 }
163       }
164    }
165    else
166    {
167       int count = chlen (actions);
168       int x = 0;
169 
170       for (x = 0; x < count; x++)
171       {
172 	 ret = injectCDKMenu (menu, actions[x]);
173 	 if (menu->exitType != vEARLY_EXIT)
174 	 {
175 	    return ret;
176 	 }
177       }
178    }
179 
180    /* Set the exit type and return. */
181    setExitType (menu, 0);
182    return -1;
183 }
184 
185 /*
186  * The "%" operator is simpler but does not handle negative values.
187  */
wrapped(int within,int limit)188 static int wrapped (int within, int limit)
189 {
190    if (within < 0)
191       within = limit - 1;
192    else if (within >= limit)
193       within = 0;
194    return within;
195 }
196 
drawTitle(CDKMENU * menu,int item)197 static void drawTitle (CDKMENU *menu, int item)
198 {
199    writeChtype (menu->titleWin[item],
200 		0, 0, menu->title[item],
201 		HORIZONTAL,
202 		0, menu->titleLen[item]);
203 }
204 
drawItem(CDKMENU * menu,int item,int offset)205 static void drawItem (CDKMENU *menu, int item, int offset)
206 {
207    writeChtype (menu->pullWin[menu->currentTitle],
208 		1, item + TITLELINES - offset,
209 		menu->sublist[menu->currentTitle][item],
210 		HORIZONTAL,
211 		0, menu->sublistLen[menu->currentTitle][item]);
212 }
213 
214 /* Highlight the current sub-menu item. */
selectItem(CDKMENU * menu,int item,int offset)215 static void selectItem (CDKMENU *menu, int item, int offset)
216 {
217    writeChtypeAttrib (menu->pullWin[menu->currentTitle],
218 		      1, item + TITLELINES - offset,
219 		      menu->sublist[menu->currentTitle][item],
220 		      menu->subtitleAttr,
221 		      HORIZONTAL,
222 		      0, menu->sublistLen[menu->currentTitle][item]);
223 }
224 
withinSubmenu(CDKMENU * menu,int step)225 static void withinSubmenu (CDKMENU *menu, int step)
226 {
227    int next = wrapped (menu->currentSubtitle + step, menu->subsize[menu->currentTitle]);
228 
229    if (next != menu->currentSubtitle)
230    {
231       CDKSCREEN *screen = ScreenOf (menu);
232       int ymax = getmaxy (screen->window);
233 
234       if ((1 +
235 	   getbegy (menu->pullWin[menu->currentTitle]) +
236 	   menu->subsize[menu->currentTitle]) >= ymax)
237       {
238 	 menu->currentSubtitle = next;
239 	 drawCDKMenuSubwin (menu);
240       }
241       else
242       {
243 	 /* Erase the old subtitle. */
244 	 drawItem (menu, menu->currentSubtitle, 0);
245 
246 	 /* Set the values. */
247 	 menu->currentSubtitle = next;
248 
249 	 /* Draw the new sub-title. */
250 	 selectItem (menu, menu->currentSubtitle, 0);
251 
252 	 wrefresh (menu->pullWin[menu->currentTitle]);
253       }
254 
255       ObjOf (menu)->inputWindow = menu->titleWin[menu->currentTitle];
256    }
257 }
258 
acrossSubmenus(CDKMENU * menu,int step)259 static void acrossSubmenus (CDKMENU *menu, int step)
260 {
261    int next = wrapped (menu->currentTitle + step, menu->menuItems);
262 
263    if (next != menu->currentTitle)
264    {
265       /* Erase the menu sub-window. */
266       eraseCDKMenuSubwin (menu);
267       refreshCDKScreen (ScreenOf (menu));
268 
269       /* Set the values. */
270       menu->currentTitle = next;
271       menu->currentSubtitle = 0;
272 
273       /* Draw the new menu sub-window. */
274       drawCDKMenuSubwin (menu);
275       ObjOf (menu)->inputWindow = menu->titleWin[menu->currentTitle];
276    }
277 }
278 
279 /*
280  * Inject a character into the menu widget.
281  */
_injectCDKMenu(CDKOBJS * object,chtype input)282 static int _injectCDKMenu (CDKOBJS *object, chtype input)
283 {
284    CDKMENU *widget = (CDKMENU *)object;
285    int ppReturn = 1;
286    int ret = unknownInt;
287    bool complete = FALSE;
288 
289    /* Set the exit type. */
290    setExitType (widget, 0);
291 
292    /* Check if there is a pre-process function to be called. */
293    if (PreProcessFuncOf (widget) != 0)
294    {
295       /* Call the pre-process function. */
296       ppReturn = PreProcessFuncOf (widget) (vMENU,
297 					    widget,
298 					    PreProcessDataOf (widget),
299 					    input);
300    }
301 
302    /* Should we continue? */
303    if (ppReturn != 0)
304    {
305       /* Check for key bindings. */
306       if (checkCDKObjectBind (vMENU, widget, input) != 0)
307       {
308 	 checkEarlyExit (widget);
309 	 complete = TRUE;
310       }
311       else
312       {
313 	 switch (input)
314 	 {
315 	 case KEY_LEFT:
316 	    acrossSubmenus (widget, -1);
317 	    break;
318 
319 	 case KEY_RIGHT:
320 	 case KEY_TAB:
321 	    acrossSubmenus (widget, 1);
322 	    break;
323 
324 	 case KEY_UP:
325 	    withinSubmenu (widget, -1);
326 	    break;
327 
328 	 case KEY_DOWN:
329 	 case SPACE:
330 	    withinSubmenu (widget, 1);
331 	    break;
332 
333 	 case KEY_ENTER:
334 	    cleanUpMenu (widget);
335 	    setExitType (widget, input);
336 	    widget->lastSelection = ((widget->currentTitle * 100) + widget->currentSubtitle);
337 	    ret = widget->lastSelection;
338 	    complete = TRUE;
339 	    break;
340 
341 	 case KEY_ESC:
342 	    cleanUpMenu (widget);
343 	    setExitType (widget, input);
344 	    widget->lastSelection = -1;
345 	    ret = widget->lastSelection;
346 	    complete = TRUE;
347 	    break;
348 
349 	 case KEY_ERROR:
350 	    setExitType (widget, input);
351 	    complete = TRUE;
352 	    break;
353 
354 	 case CDK_REFRESH:
355 	    eraseCDKScreen (ScreenOf (widget));
356 	    refreshCDKScreen (ScreenOf (widget));
357 	    break;
358 	 }
359       }
360 
361       /* Should we call a post-process? */
362       if (!complete && (PostProcessFuncOf (widget) != 0))
363       {
364 	 PostProcessFuncOf (widget) (vMENU,
365 				     widget,
366 				     PostProcessDataOf (widget),
367 				     input);
368       }
369    }
370 
371    if (!complete)
372    {
373       setExitType (widget, 0);
374    }
375 
376    ResultOf (widget).valueInt = ret;
377    return (ret != unknownInt);
378 }
379 
380 /*
381  * Draw a menu item subwindow.
382  */
drawCDKMenuSubwin(CDKMENU * menu)383 void drawCDKMenuSubwin (CDKMENU *menu)
384 {
385    int x;
386    int high = getmaxy (menu->pullWin[menu->currentTitle]) - 2;
387    int x0 = 0;
388    int x1 = menu->subsize[menu->currentTitle];
389 
390    if (x1 > high)
391       x1 = high;
392 
393    if (menu->currentSubtitle >= x1)
394    {
395       x0 = (menu->currentSubtitle - x1) + 1;
396       x1 += x0;
397    }
398 
399    /* Box the window. */
400    werase (menu->pullWin[menu->currentTitle]);
401    box (menu->pullWin[menu->currentTitle], ACS_VLINE, ACS_HLINE);
402 
403    if (menu->menuPos == BOTTOM)
404    {
405       (void)mvwaddch (menu->pullWin[menu->currentTitle],
406 		      menu->subsize[menu->currentTitle] + 1, 0, ACS_LTEE);
407    }
408    else
409    {
410       (void)mvwaddch (menu->pullWin[menu->currentTitle], 0, 0, ACS_LTEE);
411    }
412 
413    /* Draw the items. */
414    for (x = x0; x < x1; x++)
415    {
416       drawItem (menu, x, x0);
417    }
418 
419    selectItem (menu, menu->currentSubtitle, x0);
420    wrefresh (menu->pullWin[menu->currentTitle]);
421 
422    /* Highlight the title. */
423    writeChtypeAttrib (menu->titleWin[menu->currentTitle],
424 		      0, 0, menu->title[menu->currentTitle],
425 		      menu->titleAttr, HORIZONTAL, 0,
426 		      menu->titleLen[menu->currentTitle]);
427    wrefresh (menu->titleWin[menu->currentTitle]);
428 }
429 
430 /*
431  * Erase a menu item subwindow.
432  */
eraseCDKMenuSubwin(CDKMENU * menu)433 void eraseCDKMenuSubwin (CDKMENU *menu)
434 {
435    eraseCursesWindow (menu->pullWin[menu->currentTitle]);
436 
437    /* Redraw the sub-menu title. */
438    drawTitle (menu, menu->currentTitle);
439    wrefresh (menu->titleWin[menu->currentTitle]);
440 }
441 
442 /*
443  * Draw the menu.
444  */
_drawCDKMenu(CDKOBJS * object,boolean Box GCC_UNUSED)445 static void _drawCDKMenu (CDKOBJS *object, boolean Box GCC_UNUSED)
446 {
447    CDKMENU *menu = (CDKMENU *)object;
448    int x;
449 
450    /* Draw in the menu titles. */
451    for (x = 0; x < menu->menuItems; x++)
452    {
453       drawTitle (menu, x);
454       wrefresh (menu->titleWin[x]);
455    }
456 }
457 
458 /*
459  * Move the menu to the given location.
460  */
_moveCDKMenu(CDKOBJS * object,int xplace,int yplace,boolean relative,boolean refresh_flag)461 static void _moveCDKMenu (CDKOBJS *object,
462 			  int xplace,
463 			  int yplace,
464 			  boolean relative,
465 			  boolean refresh_flag)
466 {
467    CDKMENU *menu = (CDKMENU *)object;
468    /* *INDENT-EQLS* */
469    int currentX = getbegx (WindowOf (menu));
470    int currentY = getbegy (WindowOf (menu));
471    int xpos     = xplace;
472    int ypos     = yplace;
473    int xdiff    = 0;
474    int ydiff    = 0;
475    int x;
476 
477    /*
478     * If this is a relative move, then we will adjust where we want
479     * to move to.
480     */
481    if (relative)
482    {
483       xpos = getbegx (WindowOf (menu)) + xplace;
484       ypos = getbegy (WindowOf (menu)) + yplace;
485    }
486 
487    /* Adjust the window if we need to. */
488    alignxy (WindowOf (menu),
489 	    &xpos,
490 	    &ypos,
491 	    getmaxx (WindowOf (menu)),
492 	    getmaxy (WindowOf (menu)));
493 
494    /* Get the difference. */
495    xdiff = currentX - xpos;
496    ydiff = currentY - ypos;
497 
498    /* Move the windows to the new location. */
499    moveCursesWindow (WindowOf (menu), -xdiff, -ydiff);
500    for (x = 0; x < menu->menuItems; x++)
501    {
502       moveCursesWindow (menu->titleWin[x], -xdiff, -ydiff);
503    }
504 
505    /* Touch the windows so they 'move'. */
506    refreshCDKWindow (WindowOf (menu));
507 
508    /* Redraw the window, if they asked for it. */
509    if (refresh_flag)
510    {
511       drawCDKMenu (menu, ObjOf (menu)->box);
512    }
513 }
514 
515 /*
516  * Set the background attribute of the widget.
517  */
_setBKattrMenu(CDKOBJS * object,chtype attrib)518 static void _setBKattrMenu (CDKOBJS *object, chtype attrib)
519 {
520    if (object != 0)
521    {
522       CDKMENU *widget = (CDKMENU *)object;
523       int x;
524 
525       for (x = 0; x < widget->menuItems; x++)
526       {
527 	 wbkgd (widget->titleWin[x], attrib);
528 	 wbkgd (widget->pullWin[x], attrib);
529       }
530    }
531 }
532 
533 /*
534  * Destroy a menu widget.
535  */
_destroyCDKMenu(CDKOBJS * object)536 static void _destroyCDKMenu (CDKOBJS *object)
537 {
538    if (object != 0)
539    {
540       CDKMENU *menu = (CDKMENU *)object;
541       int x, y;
542 
543       /* Clean up both the winodws and the char pointers. */
544       for (x = 0; x < menu->menuItems; x++)
545       {
546 	 /* Clean the windows. */
547 	 deleteCursesWindow (menu->titleWin[x]);
548 	 deleteCursesWindow (menu->pullWin[x]);
549 
550 	 /* Delete the character pointers. */
551 	 freeChtype (menu->title[x]);
552 	 for (y = 0; y < menu->subsize[x]; y++)
553 	 {
554 	    freeChtype (menu->sublist[x][y]);
555 	 }
556       }
557 
558       /* Clean the key bindings. */
559       cleanCDKObjectBindings (vMENU, menu);
560 
561       /* Unregister this object. */
562       unregisterCDKObject (vMENU, menu);
563    }
564 }
565 
566 /*
567  * Erase the menu widget from the screen.
568  */
_eraseCDKMenu(CDKOBJS * object)569 static void _eraseCDKMenu (CDKOBJS *object)
570 {
571    if (validCDKObject (object))
572    {
573       CDKMENU *menu = (CDKMENU *)object;
574       int x = 0;
575 
576       for (x = 0; x < menu->menuItems; x++)
577       {
578 	 werase (menu->titleWin[x]);
579 	 wrefresh (menu->titleWin[x]);
580 	 werase (menu->pullWin[x]);
581 	 wrefresh (menu->pullWin[x]);
582       }
583    }
584 }
585 
586 /*
587  * Set multiple features of the menu.
588  */
setCDKMenu(CDKMENU * menu,int menuItem,int subMenuItem,chtype titleHighlight,chtype subTitleHighlight)589 void setCDKMenu (CDKMENU *menu,
590 		 int menuItem,
591 		 int subMenuItem,
592 		 chtype titleHighlight,
593 		 chtype subTitleHighlight)
594 {
595    setCDKMenuCurrentItem (menu, menuItem, subMenuItem);
596    setCDKMenuTitleHighlight (menu, titleHighlight);
597    setCDKMenuSubTitleHighlight (menu, subTitleHighlight);
598 }
599 
600 /*
601  * Set the current menu item to highlight.
602  */
setCDKMenuCurrentItem(CDKMENU * menu,int menuitem,int submenuitem)603 void setCDKMenuCurrentItem (CDKMENU *menu, int menuitem, int submenuitem)
604 {
605    /* *INDENT-EQLS* */
606    menu->currentTitle    = wrapped (menuitem, menu->menuItems);
607    menu->currentSubtitle = wrapped (submenuitem, menu->subsize[menu->currentTitle]);
608 }
getCDKMenuCurrentItem(CDKMENU * menu,int * menuItem,int * subMenuItem)609 void getCDKMenuCurrentItem (CDKMENU *menu, int *menuItem, int *subMenuItem)
610 {
611    /* *INDENT-EQLS* */
612    (*menuItem)          = menu->currentTitle;
613    (*subMenuItem)       = menu->currentSubtitle;
614 }
615 
616 /*
617  * Set the attribute of the menu titles.
618  */
setCDKMenuTitleHighlight(CDKMENU * menu,chtype highlight)619 void setCDKMenuTitleHighlight (CDKMENU *menu, chtype highlight)
620 {
621    menu->titleAttr = highlight;
622 }
getCDKMenuTitleHighlight(CDKMENU * menu)623 chtype getCDKMenuTitleHighlight (CDKMENU *menu)
624 {
625    return menu->titleAttr;
626 }
627 
628 /*
629  * Set the attribute of the sub-title.
630  */
setCDKMenuSubTitleHighlight(CDKMENU * menu,chtype highlight)631 void setCDKMenuSubTitleHighlight (CDKMENU *menu, chtype highlight)
632 {
633    menu->subtitleAttr = highlight;
634 }
getCDKMenuSubTitleHighlight(CDKMENU * menu)635 chtype getCDKMenuSubTitleHighlight (CDKMENU *menu)
636 {
637    return menu->subtitleAttr;
638 }
639 
640 /*
641  * Exit the menu.
642  */
cleanUpMenu(CDKMENU * menu)643 static void cleanUpMenu (CDKMENU *menu)
644 {
645    /* Erase the sub-menu. */
646    eraseCDKMenuSubwin (menu);
647    wrefresh (menu->pullWin[menu->currentTitle]);
648 
649    /* Refresh the screen. */
650    refreshCDKScreen (ScreenOf (menu));
651 }
652 
_focusCDKMenu(CDKOBJS * object)653 static void _focusCDKMenu (CDKOBJS *object)
654 {
655    CDKMENU *menu = (CDKMENU *)object;
656 
657    drawCDKMenuSubwin (menu);
658    ObjOf (menu)->inputWindow = menu->titleWin[menu->currentTitle];
659 }
660 
661 dummyUnfocus (Menu)
662 
663 dummyRefreshData (Menu)
664 
665 dummySaveData (Menu)
666