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