1 /* -*- c -*- ------------------------------------------------------------- *
3 * Copyright 2004-2005 Murali Krishnan Ganapathy - All Rights Reserved
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
8 * Boston MA 02111-1307, USA; either version 2 of the License, or
9 * (at your option) any later version; incorporated herein by reference.
11 * ----------------------------------------------------------------------- */
19 static pt_menusystem ms
; // Pointer to the menusystem
21 "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
22 char TITLELONG
[] = " TITLE too long ";
23 char ITEMLONG
[] = " ITEM too long ";
24 char ACTIONLONG
[] = " ACTION too long ";
25 char STATUSLONG
[] = " STATUS too long ";
28 /* Forward declarations */
29 int calc_visible(pt_menu menu
, int first
);
30 int next_visible(pt_menu menu
, int index
);
31 int prev_visible(pt_menu menu
, int index
);
32 int next_visible_sep(pt_menu menu
, int index
);
33 int prev_visible_sep(pt_menu menu
, int index
);
34 int calc_first_early(pt_menu menu
, int curr
);
35 int calc_first_late(pt_menu menu
, int curr
);
36 int isvisible(pt_menu menu
, int first
, int curr
);
38 /* Basic Menu routines */
40 // This is same as inputc except it honors the ontimeout handler
41 // and calls it when needed. For the callee, there is no difference
42 // as this will not return unless a key has been pressed.
43 static int getch(void)
49 // Wait until keypress if no handler specified
50 if ((ms
->ontimeout
== NULL
) && (ms
->ontotaltimeout
== NULL
))
51 return get_key(stdin
, 0);
55 for (i
= 0; i
< ms
->tm_numsteps
; i
++) {
56 key
= get_key(stdin
, ms
->tm_stepsize
);
60 if ((ms
->tm_total_timeout
== 0) || (ms
->ontotaltimeout
== NULL
))
61 continue; // Dont bother with calculations if no handler
62 ms
->tm_sofar_timeout
+= ms
->tm_stepsize
;
63 if (ms
->tm_sofar_timeout
>= ms
->tm_total_timeout
) {
64 th
= ms
->ontotaltimeout
;
65 ms
->tm_sofar_timeout
= 0;
66 break; // Get out of the for loop
70 continue; // no handler
73 case CODE_ENTER
: // Pretend user hit enter
75 case CODE_ESCAPE
: // Pretend user hit escape
84 int find_shortcut(pt_menu menu
, uchar shortcut
, int index
)
85 // Find the next index with specified shortcut key
90 // Garbage in garbage out
91 if ((index
< 0) || (index
>= menu
->numitems
))
94 // Go till end of menu
95 while (ans
< menu
->numitems
) {
96 mi
= menu
->items
[ans
];
97 if ((mi
->action
== OPT_INVISIBLE
) || (mi
->action
== OPT_SEP
)
98 || (mi
->shortcut
!= shortcut
))
103 // Start at the beginning and try again
105 while (ans
< index
) {
106 mi
= menu
->items
[ans
];
107 if ((mi
->action
== OPT_INVISIBLE
) || (mi
->action
== OPT_SEP
)
108 || (mi
->shortcut
!= shortcut
))
113 return index
; // Sorry not found
116 /* Redraw background and title */
117 static void reset_ui(void)
122 clearwindow(ms
->minrow
, ms
->mincol
, ms
->maxrow
, ms
->maxcol
,
123 ms
->fillchar
, ms
->fillattr
);
125 tpos
= (ms
->numcols
- strlen(ms
->title
) - 1) >> 1; // center it on line
126 gotoxy(ms
->minrow
, ms
->mincol
);
127 cprint(ms
->tfillchar
, ms
->titleattr
, ms
->numcols
);
128 gotoxy(ms
->minrow
, ms
->mincol
+ tpos
);
129 csprint(ms
->title
, ms
->titleattr
);
137 * attr[0] is non-hilite attr, attr[1] is highlight attr
139 void printmenuitem(const char *str
, uchar
* attr
)
141 int hlite
= NOHLITE
; // Initially no highlighting
145 case BELL
: // No Bell Char
147 case ENABLEHLITE
: // Switch on highlighting
150 case DISABLEHLITE
: // Turn off highlighting
154 putch(*str
, attr
[hlite
]);
162 * print_line - Print a whole line in a menu
163 * @menu: current menu to handle
164 * @curr: index of the current entry highlighted
165 * @top: top coordinate of the @menu
166 * @left: left coordinate of the @menu
167 * @x: index in the menu of curr
168 * @row: row currently displayed
169 * @radio: radio item?
171 static void print_line(pt_menu menu
, int curr
, uchar top
, uchar left
,
172 int x
, int row
, bool radio
)
175 char fchar
[6], lchar
[6]; // The first and last char in for each entry
176 const char *str
; // Item string (cf printmenuitem)
177 char sep
[MENULEN
]; // Separator (OPT_SEP)
178 uchar
*attr
; // Attribute
179 int menuwidth
= menu
->menuwidth
+ 3;
181 if (row
>= menu
->menuheight
)
186 memset(sep
, ms
->box_horiz
, menuwidth
);
187 sep
[menuwidth
- 1] = 0;
189 // Setup the defaults now
193 fchar
[2] = (x
== curr
? RADIOSEL
: RADIOUNSEL
);
195 fchar
[4] = '\0'; // Unselected ( )
196 lchar
[0] = '\0'; // Nothing special after
197 attr
= ms
->normalattr
; // Always same attribute
199 lchar
[0] = fchar
[0] = ' ';
200 lchar
[1] = fchar
[1] = '\0'; // fchar and lchar are just spaces
201 attr
= (x
== curr
? ms
->reverseattr
: ms
->normalattr
); // Normal attributes
203 str
= ci
->item
; // Pointer to item string
204 switch (ci
->action
) // set up attr,str,fchar,lchar for everything
208 attr
= ms
->inactattr
;
210 attr
= (x
== curr
? ms
->revinactattr
: ms
->inactattr
);
214 break; // Not supported for radio menu
220 break; // Not supported for radio menu
221 lchar
[0] = RADIOMENUCHAR
;
226 break; // Not supported for radio menu
229 lchar
[2] = (ci
->itemdata
.checked
? CHECKED
: UNCHECKED
);
236 fchar
[2] = LEFT_MIDDLE_BORDER
;
237 fchar
[3] = MIDDLE_BORDER
;
238 fchar
[4] = MIDDLE_BORDER
;
240 memset(sep
, MIDDLE_BORDER
, menuwidth
);
241 sep
[menuwidth
- 1] = 0;
243 lchar
[0] = MIDDLE_BORDER
;
244 lchar
[1] = RIGHT_MIDDLE_BORDER
;
250 break; // Not supported for radio menu
254 default: // Just to keep the compiler happy
258 // Wipe area with spaces
259 gotoxy(top
+ row
, left
- 2);
260 cprint(ms
->spacechar
, attr
[NOHLITE
], menuwidth
+ 2);
263 gotoxy(top
+ row
, left
- 2);
264 csprint(fchar
, attr
[NOHLITE
]);
267 gotoxy(top
+ row
, left
);
268 printmenuitem(str
, attr
);
271 gotoxy(top
+ row
, left
+ menuwidth
- 1);
272 csprint(lchar
, attr
[NOHLITE
]);
275 // print the menu starting from FIRST
276 // will print a maximum of menu->menuheight items
277 static void printmenu(pt_menu menu
, int curr
, uchar top
, uchar left
, uchar first
, bool radio
)
279 int x
, row
; // x = index, row = position from top
280 int numitems
, menuwidth
;
283 numitems
= calc_visible(menu
, first
);
284 if (numitems
> menu
->menuheight
)
285 numitems
= menu
->menuheight
;
287 menuwidth
= menu
->menuwidth
+ 3;
288 clearwindow(top
, left
- 2, top
+ numitems
+ 1, left
+ menuwidth
+ 1,
289 ms
->fillchar
, ms
->shadowattr
);
290 drawbox(top
- 1, left
- 3, top
+ numitems
, left
+ menuwidth
,
291 ms
->normalattr
[NOHLITE
]);
294 x
= (menuwidth
- strlen(menu
->title
) - 1) >> 1;
295 gotoxy(top
- 1, left
+ x
);
296 printmenuitem(menu
->title
, ms
->normalattr
);
298 // All lines in the menu
299 row
= -1; // 1 less than inital value of x
300 for (x
= first
; x
< menu
->numitems
; x
++) {
302 if (ci
->action
== OPT_INVISIBLE
)
306 break; // Already have enough number of items
307 print_line(menu
, curr
, top
, left
, x
, row
, radio
);
309 // Check if we need to MOREABOVE and MOREBELOW to be added
312 x
= next_visible_sep(menu
, 0); // First item
313 if (!isvisible(menu
, first
, x
)) // There is more above
316 gotoxy(top
, left
+ menuwidth
);
317 cprint(MOREABOVE
, ms
->normalattr
[NOHLITE
], 1);
319 x
= prev_visible_sep(menu
, menu
->numitems
); // last item
320 if (!isvisible(menu
, first
, x
)) // There is more above
323 gotoxy(top
+ numitems
- 1, left
+ menuwidth
);
324 cprint(MOREBELOW
, ms
->normalattr
[NOHLITE
], 1);
327 x
= ((numitems
- 1) * curr
) / (menu
->numitems
);
328 if ((x
> 0) && (row
== 1)) {
329 gotoxy(top
+ x
, left
+ menuwidth
);
330 csprint("\016\141\017", ms
->normalattr
[NOHLITE
]);
333 ms
->handler(ms
, menu
->items
[curr
]);
336 void cleanupmenu(pt_menu menu
, uchar top
, uchar left
, int numitems
)
338 if (numitems
> menu
->menuheight
)
339 numitems
= menu
->menuheight
;
340 clearwindow(top
, left
- 2, top
+ numitems
+ 1, left
+ menu
->menuwidth
+ 4, ms
->fillchar
, ms
->fillattr
); // Clear the shadow
341 clearwindow(top
- 1, left
- 3, top
+ numitems
, left
+ menu
->menuwidth
+ 3, ms
->fillchar
, ms
->fillattr
); // main window
345 /* Handle one menu */
346 static pt_menuitem
getmenuoption(pt_menu menu
, uchar top
, uchar left
, uchar startopt
, bool radio
)
347 // Return item chosen or NULL if ESC was hit.
349 int prev
, prev_first
, curr
, i
, first
, tmp
;
351 bool redraw
= true; // Need to draw the menu the first time
353 pt_menuitem ci
; // Current item
354 t_handler_return hr
; // Return value of handler
356 numitems
= calc_visible(menu
, 0);
358 gotoxy(ms
->minrow
+ ms
->statline
, ms
->mincol
);
359 cprint(ms
->spacechar
, ms
->statusattr
[NOHLITE
], ms
->numcols
);
361 // Initialise current menu item
362 curr
= next_visible(menu
, startopt
);
365 gotoxy(ms
->minrow
+ ms
->statline
, ms
->mincol
);
366 cprint(ms
->spacechar
, ms
->statusattr
[NOHLITE
], ms
->numcols
);
367 gotoxy(ms
->minrow
+ ms
->statline
, ms
->mincol
);
368 printmenuitem(menu
->items
[curr
]->status
, ms
->statusattr
);
369 first
= calc_first_early(menu
, curr
);
373 /* Redraw everything if:
374 * + we need to scroll (take care of scroll bars, ...)
377 if (prev_first
!= first
|| redraw
) {
378 printmenu(menu
, curr
, top
, left
, first
, radio
);
380 /* Redraw only the highlighted entry */
381 print_line(menu
, curr
, top
, left
, prev
, prev
- first
, radio
);
382 print_line(menu
, curr
, top
, left
, curr
, curr
- first
, radio
);
387 ci
= menu
->items
[curr
];
394 curr
= next_visible(menu
, 0);
395 first
= calc_first_early(menu
, curr
);
398 curr
= prev_visible(menu
, numitems
- 1);
399 first
= calc_first_late(menu
, curr
);
402 for (i
= 0; i
< 5; i
++)
403 curr
= next_visible(menu
, curr
+ 1);
404 first
= calc_first_late(menu
, curr
);
407 for (i
= 0; i
< 5; i
++)
408 curr
= prev_visible(menu
, curr
- 1);
409 first
= calc_first_early(menu
, curr
);
412 curr
= prev_visible(menu
, curr
- 1);
414 first
= calc_first_early(menu
, curr
);
417 curr
= next_visible(menu
, curr
+ 1);
418 if (!isvisible(menu
, first
, curr
))
419 first
= calc_first_late(menu
, curr
);
427 if (ci
->action
== OPT_INACTIVE
)
429 if (ci
->action
== OPT_CHECKBOX
)
431 if (ci
->action
== OPT_SEP
)
433 if (ci
->action
== OPT_EXITMENU
)
434 return NULL
; // As if we hit Esc
435 // If we are going into a radio menu, dont call handler, return ci
436 if (ci
->action
== OPT_RADIOMENU
)
438 if (ci
->handler
!= NULL
) // Do we have a handler
440 hr
= ci
->handler(ms
, ci
);
441 if (hr
.refresh
) // Do we need to refresh
443 // Cleanup menu using old number of items
444 cleanupmenu(menu
, top
, left
, numitems
);
445 // Recalculate the number of items
446 numitems
= calc_visible(menu
, 0);
448 printmenu(menu
, curr
, top
, left
, first
, radio
);
456 if (ci
->action
!= OPT_CHECKBOX
)
458 ci
->itemdata
.checked
= !ci
->itemdata
.checked
;
459 if (ci
->handler
!= NULL
) // Do we have a handler
461 hr
= ci
->handler(ms
, ci
);
462 if (hr
.refresh
) // Do we need to refresh
464 // Cleanup menu using old number of items
465 cleanupmenu(menu
, top
, left
, numitems
);
466 // Recalculate the number of items
467 numitems
= calc_visible(menu
, 0);
469 printmenu(menu
, curr
, top
, left
, first
, radio
);
474 // Check if this is a shortcut key
475 if (((asc
>= 'A') && (asc
<= 'Z')) ||
476 ((asc
>= 'a') && (asc
<= 'z')) ||
477 ((asc
>= '0') && (asc
<= '9'))) {
478 tmp
= find_shortcut(menu
, asc
, curr
);
479 if ((tmp
> curr
) && (!isvisible(menu
, first
, tmp
)))
480 first
= calc_first_late(menu
, tmp
);
482 first
= calc_first_early(menu
, tmp
);
485 if (ms
->keys_handler
) // Call extra keys handler
486 ms
->keys_handler(ms
, menu
->items
[curr
], asc
);
488 /* The handler may have changed the UI, reset it on exit */
490 // Cleanup menu using old number of items
491 cleanupmenu(menu
, top
, left
, numitems
);
492 // Recalculate the number of items
493 numitems
= calc_visible(menu
, 0);
495 printmenu(menu
, curr
, top
, left
, first
, radio
);
499 // Update status line
500 /* Erase the previous status */
501 gotoxy(ms
->minrow
+ ms
->statline
, ms
->mincol
);
502 cprint(ms
->spacechar
, ms
->statusattr
[NOHLITE
], ms
->numcols
);
503 /* Print the new status */
504 gotoxy(ms
->minrow
+ ms
->statline
, ms
->mincol
);
505 printmenuitem(menu
->items
[curr
]->status
, ms
->statusattr
);
507 return NULL
; // Should never come here
510 /* Handle the entire system of menu's. */
511 pt_menuitem
runmenusystem(uchar top
, uchar left
, pt_menu cmenu
, uchar startopt
,
515 * Which menu should be currently displayed
517 * What is the position of the top,left corner of the menu
519 * which menu item do I start with
521 * NORMALMENU or RADIOMENU
524 * Returns a pointer to the final item chosen, or NULL if nothing chosen.
527 pt_menuitem opt
, choice
;
535 // Set the menu height
536 cmenu
->menuheight
= ms
->maxrow
- top
- 3;
537 if (cmenu
->menuheight
> ms
->maxmenuheight
)
538 cmenu
->menuheight
= ms
->maxmenuheight
;
539 if (menutype
== NORMALMENU
)
540 opt
= getmenuoption(cmenu
, top
, left
, startopt
, false);
541 else // menutype == RADIOMENU
542 opt
= getmenuoption(cmenu
, top
, left
, startopt
, true);
546 cleanupmenu(cmenu
, top
, left
, calc_visible(cmenu
, 0));
549 // Are we done with the menu system?
550 if ((opt
->action
!= OPT_SUBMENU
) && (opt
->action
!= OPT_RADIOMENU
)) {
551 cleanupmenu(cmenu
, top
, left
, calc_visible(cmenu
, 0));
552 return opt
; // parent cleanup other menus
554 // Either radiomenu or submenu
555 // Do we have a valid menu number? The next hack uses the fact that
556 // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
557 if (opt
->itemdata
.submenunum
>= ms
->nummenus
) // This is Bad....
559 gotoxy(12, 12); // Middle of screen
560 csprint("ERROR: Invalid submenu requested.", 0x07);
561 cleanupmenu(cmenu
, top
, left
, calc_visible(cmenu
, 0));
562 return NULL
; // Pretend user hit esc
564 // Call recursively for submenu
565 // Position the submenu below the current item,
566 // covering half the current window (horizontally)
567 row
= ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
]->row
;
568 col
= ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
]->col
;
570 row
= top
+ opt
->index
+ 2;
572 col
= left
+ 3 + (cmenu
->menuwidth
>> 1);
573 mt
= (opt
->action
== OPT_SUBMENU
? NORMALMENU
: RADIOMENU
);
575 if ((opt
->action
== OPT_RADIOMENU
) && (opt
->data
!= NULL
))
576 startat
= ((t_menuitem
*) opt
->data
)->index
;
578 choice
= runmenusystem(row
, col
,
579 ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
],
581 if (opt
->action
== OPT_RADIOMENU
) {
583 opt
->data
= (void *)choice
; // store choice in data field
584 if (opt
->handler
!= NULL
)
585 opt
->handler(ms
, opt
);
586 choice
= NULL
; // Pretend user hit esc
588 if (choice
== NULL
) // User hit Esc in submenu
591 startopt
= opt
->index
;
594 cleanupmenu(cmenu
, top
, left
, calc_visible(cmenu
, 0));
599 // Finds the indexof the menu with given name
600 uchar
find_menu_num(const char *name
)
607 for (i
= 0; i
< ms
->nummenus
; i
++) {
609 if ((m
->name
) && (strcmp(m
->name
, name
) == 0))
615 // Run through all items and if they are submenus
616 // with a non-trivial "action" and trivial submenunum
617 // replace submenunum with the menu with name "action"
618 void fix_submenus(void)
625 for (i
= 0; i
< ms
->nummenus
; i
++) {
627 for (j
= 0; j
< m
->numitems
; j
++) {
629 // if item is a submenu and has non-empty non-trivial data string
630 if (mi
->data
&& strlen(mi
->data
) > 0 &&
631 ((mi
->action
== OPT_SUBMENU
)
632 || (mi
->action
== OPT_RADIOMENU
))) {
633 mi
->itemdata
.submenunum
= find_menu_num(mi
->data
);
639 /* User Callable functions */
641 pt_menuitem
showmenus(uchar startmenu
)
645 fix_submenus(); // Fix submenu numbers incase nick names were used
647 /* Turn autowrap off, to avoid scrolling the menu */
650 // Setup screen for menusystem
653 // Go, main menu cannot be a radio menu
654 rv
= runmenusystem(ms
->minrow
+ MENUROW
, ms
->mincol
+ MENUCOL
,
655 ms
->menus
[(unsigned int)startmenu
], 0, NORMALMENU
);
657 // Hide the garbage we left on the screen
659 gotoxy(ms
->minrow
, ms
->mincol
);
662 // Return user choice
666 pt_menusystem
init_menusystem(const char *title
)
671 ms
= (pt_menusystem
) malloc(sizeof(t_menusystem
));
675 // Initialise all menu pointers
676 for (i
= 0; i
< MAXMENUS
; i
++)
679 ms
->title
= (char *)malloc(TITLELEN
+ 1);
681 strcpy(ms
->title
, TITLESTR
); // Copy string
683 strcpy(ms
->title
, title
);
686 ms
->tm_stepsize
= TIMEOUTSTEPSIZE
;
687 ms
->tm_numsteps
= TIMEOUTNUMSTEPS
;
689 ms
->normalattr
[NOHLITE
] = NORMALATTR
;
690 ms
->normalattr
[HLITE
] = NORMALHLITE
;
692 ms
->reverseattr
[NOHLITE
] = REVERSEATTR
;
693 ms
->reverseattr
[HLITE
] = REVERSEHLITE
;
695 ms
->inactattr
[NOHLITE
] = INACTATTR
;
696 ms
->inactattr
[HLITE
] = INACTHLITE
;
698 ms
->revinactattr
[NOHLITE
] = REVINACTATTR
;
699 ms
->revinactattr
[HLITE
] = REVINACTHLITE
;
701 ms
->statusattr
[NOHLITE
] = STATUSATTR
;
702 ms
->statusattr
[HLITE
] = STATUSHLITE
;
704 ms
->statline
= STATLINE
;
705 ms
->tfillchar
= TFILLCHAR
;
706 ms
->titleattr
= TITLEATTR
;
708 ms
->fillchar
= FILLCHAR
;
709 ms
->fillattr
= FILLATTR
;
710 ms
->spacechar
= SPACECHAR
;
711 ms
->shadowattr
= SHADOWATTR
;
713 ms
->menupage
= MENUPAGE
; // Usually no need to change this at all
715 // Initialise all handlers
717 ms
->keys_handler
= NULL
;
718 ms
->ontimeout
= NULL
; // No timeout handler
719 ms
->tm_total_timeout
= 0;
720 ms
->tm_sofar_timeout
= 0;
721 ms
->ontotaltimeout
= NULL
;
723 // Setup ACTION_{,IN}VALID
724 ACTION_VALID
.valid
= 1;
725 ACTION_VALID
.refresh
= 0;
726 ACTION_INVALID
.valid
= 0;
727 ACTION_INVALID
.refresh
= 0;
729 // Figure out the size of the screen we are in now.
730 // By default we use the whole screen for our menu
731 if (getscreensize(1, &ms
->numrows
, &ms
->numcols
)) {
732 /* Unknown screen size? */
736 ms
->minrow
= ms
->mincol
= 0;
737 ms
->maxcol
= ms
->numcols
- 1;
738 ms
->maxrow
= ms
->numrows
- 1;
740 // How many entries per menu can we display at a time
741 ms
->maxmenuheight
= ms
->maxrow
- ms
->minrow
- 3;
742 if (ms
->maxmenuheight
> MAXMENUHEIGHT
)
743 ms
->maxmenuheight
= MAXMENUHEIGHT
;
750 void set_normal_attr(uchar normal
, uchar selected
, uchar inactivenormal
,
751 uchar inactiveselected
)
754 ms
->normalattr
[0] = normal
;
755 if (selected
!= 0xFF)
756 ms
->reverseattr
[0] = selected
;
757 if (inactivenormal
!= 0xFF)
758 ms
->inactattr
[0] = inactivenormal
;
759 if (inactiveselected
!= 0xFF)
760 ms
->revinactattr
[0] = inactiveselected
;
763 void set_normal_hlite(uchar normal
, uchar selected
, uchar inactivenormal
,
764 uchar inactiveselected
)
767 ms
->normalattr
[1] = normal
;
768 if (selected
!= 0xFF)
769 ms
->reverseattr
[1] = selected
;
770 if (inactivenormal
!= 0xFF)
771 ms
->inactattr
[1] = inactivenormal
;
772 if (inactiveselected
!= 0xFF)
773 ms
->revinactattr
[1] = inactiveselected
;
776 void set_status_info(uchar statusattr
, uchar statushlite
, uchar statline
)
778 if (statusattr
!= 0xFF)
779 ms
->statusattr
[NOHLITE
] = statusattr
;
780 if (statushlite
!= 0xFF)
781 ms
->statusattr
[HLITE
] = statushlite
;
782 // statline is relative to minrow
783 if (statline
>= ms
->numrows
)
784 statline
= ms
->numrows
- 1;
785 ms
->statline
= statline
; // relative to ms->minrow, 0 based
788 void set_title_info(uchar tfillchar
, uchar titleattr
)
790 if (tfillchar
!= 0xFF)
791 ms
->tfillchar
= tfillchar
;
792 if (titleattr
!= 0xFF)
793 ms
->titleattr
= titleattr
;
796 void set_misc_info(uchar fillchar
, uchar fillattr
, uchar spacechar
,
799 if (fillchar
!= 0xFF)
800 ms
->fillchar
= fillchar
;
801 if (fillattr
!= 0xFF)
802 ms
->fillattr
= fillattr
;
803 if (spacechar
!= 0xFF)
804 ms
->spacechar
= spacechar
;
805 if (shadowattr
!= 0xFF)
806 ms
->shadowattr
= shadowattr
;
809 void set_menu_options(uchar maxmenuheight
)
811 if (maxmenuheight
!= 0xFF)
812 ms
->maxmenuheight
= maxmenuheight
;
815 // Set the window which menusystem should use
816 void set_window_size(uchar top
, uchar left
, uchar bot
, uchar right
)
820 if ((top
> bot
) || (left
> right
))
821 return; // Sorry no change will happen here
823 if (getscreensize(1, &nr
, &nc
)) {
824 /* Unknown screen size? */
836 ms
->numcols
= right
- left
+ 1;
837 ms
->numrows
= bot
- top
+ 1;
838 if (ms
->statline
>= ms
->numrows
)
839 ms
->statline
= ms
->numrows
- 1; // Clip statline if need be
842 void reg_handler(t_handler htype
, void *handler
)
844 // If bad value set to default screen handler
847 ms
->keys_handler
= (t_keys_handler
) handler
;
850 ms
->handler
= (t_menusystem_handler
) handler
;
855 void unreg_handler(t_handler htype
)
859 ms
->keys_handler
= NULL
;
867 void reg_ontimeout(t_timeout_handler handler
, unsigned int numsteps
,
868 unsigned int stepsize
)
870 ms
->ontimeout
= handler
;
872 ms
->tm_numsteps
= numsteps
;
874 ms
->tm_stepsize
= stepsize
;
877 void unreg_ontimeout(void)
879 ms
->ontimeout
= NULL
;
882 void reg_ontotaltimeout(t_timeout_handler handler
,
883 unsigned long numcentiseconds
)
885 if (numcentiseconds
!= 0) {
886 ms
->ontotaltimeout
= handler
;
887 ms
->tm_total_timeout
= numcentiseconds
* 10; // to convert to milliseconds
888 ms
->tm_sofar_timeout
= 0;
892 void unreg_ontotaltimeout(void)
894 ms
->ontotaltimeout
= NULL
;
897 int next_visible(pt_menu menu
, int index
)
902 else if (index
>= menu
->numitems
)
903 ans
= menu
->numitems
- 1;
906 while ((ans
< menu
->numitems
- 1) &&
907 ((menu
->items
[ans
]->action
== OPT_INVISIBLE
) ||
908 (menu
->items
[ans
]->action
== OPT_SEP
)))
913 int prev_visible(pt_menu menu
, int index
) // Return index of prev visible
918 else if (index
>= menu
->numitems
)
919 ans
= menu
->numitems
- 1;
923 ((menu
->items
[ans
]->action
== OPT_INVISIBLE
) ||
924 (menu
->items
[ans
]->action
== OPT_SEP
)))
929 int next_visible_sep(pt_menu menu
, int index
)
934 else if (index
>= menu
->numitems
)
935 ans
= menu
->numitems
- 1;
938 while ((ans
< menu
->numitems
- 1) &&
939 (menu
->items
[ans
]->action
== OPT_INVISIBLE
))
944 int prev_visible_sep(pt_menu menu
, int index
) // Return index of prev visible
949 else if (index
>= menu
->numitems
)
950 ans
= menu
->numitems
- 1;
953 while ((ans
> 0) && (menu
->items
[ans
]->action
== OPT_INVISIBLE
))
958 int calc_visible(pt_menu menu
, int first
)
965 for (i
= first
; i
< menu
->numitems
; i
++)
966 if (menu
->items
[i
]->action
!= OPT_INVISIBLE
)
971 // is curr visible if first entry is first?
972 int isvisible(pt_menu menu
, int first
, int curr
)
976 return (calc_visible(menu
, first
) - calc_visible(menu
, curr
) <
980 // Calculate the first entry to be displayed
981 // so that curr is visible and make curr as late as possible
982 int calc_first_late(pt_menu menu
, int curr
)
986 nv
= calc_visible(menu
, 0);
987 if (nv
<= menu
->menuheight
)
989 // Start with curr and go back menu->menuheight times
991 for (i
= 0; i
< menu
->menuheight
; i
++)
992 ans
= prev_visible_sep(menu
, ans
- 1);
996 // Calculate the first entry to be displayed
997 // so that curr is visible and make curr as early as possible
998 int calc_first_early(pt_menu menu
, int curr
)
1002 nv
= calc_visible(menu
, 0);
1003 if (nv
<= menu
->menuheight
)
1005 // Start with curr and go back till >= menu->menuheight
1006 // items are visible
1007 nv
= calc_visible(menu
, curr
); // Already nv of them are visible
1009 for (i
= 0; i
< menu
->menuheight
- nv
; i
++)
1010 ans
= prev_visible_sep(menu
, ans
- 1);
1014 // Create a new menu and return its position
1015 uchar
add_menu(const char *title
, int maxmenusize
)
1021 if (num
>= MAXMENUS
)
1024 m
= (pt_menu
) malloc(sizeof(t_menu
));
1032 if (maxmenusize
< 1)
1033 m
->maxmenusize
= MAXMENUSIZE
;
1035 m
->maxmenusize
= maxmenusize
;
1036 m
->items
= (pt_menuitem
*) malloc(sizeof(pt_menuitem
) * (m
->maxmenusize
));
1037 for (i
= 0; i
< m
->maxmenusize
; i
++)
1040 m
->title
= (char *)malloc(MENULEN
+ 1);
1042 if (strlen(title
) > MENULEN
- 2)
1043 strcpy(m
->title
, TITLELONG
);
1045 strcpy(m
->title
, title
);
1047 strcpy(m
->title
, EMPTYSTR
);
1048 m
->menuwidth
= strlen(m
->title
);
1050 return ms
->nummenus
- 1;
1053 void set_menu_name(const char *name
) // Set the "name" of this menu
1057 m
= ms
->menus
[ms
->nummenus
- 1];
1058 if (m
->name
) // Free up previous name
1065 m
->name
= (char *)malloc(strlen(name
) + 1);
1066 strcpy(m
->name
, name
);
1070 // Create a new named menu and return its position
1071 uchar
add_named_menu(const char *name
, const char *title
, int maxmenusize
)
1073 add_menu(title
, maxmenusize
);
1074 set_menu_name(name
);
1075 return ms
->nummenus
- 1;
1078 void set_menu_pos(uchar row
, uchar col
) // Set the position of this menu.
1082 m
= ms
->menus
[ms
->nummenus
- 1];
1087 pt_menuitem
add_sep(void) // Add a separator to current menu
1092 m
= (ms
->menus
[ms
->nummenus
- 1]);
1094 mi
= (pt_menuitem
) malloc(sizeof(t_menuitem
));
1097 m
->items
[(unsigned int)m
->numitems
] = mi
;
1098 mi
->handler
= NULL
; // No handler
1099 mi
->item
= mi
->status
= mi
->data
= NULL
;
1100 mi
->action
= OPT_SEP
;
1101 mi
->index
= m
->numitems
++;
1102 mi
->parindex
= ms
->nummenus
- 1;
1108 // Add item to the "current" menu
1109 pt_menuitem
add_item(const char *item
, const char *status
, t_action action
,
1110 const char *data
, uchar itemdata
)
1115 uchar inhlite
= 0; // Are we inside hlite area
1117 m
= (ms
->menus
[ms
->nummenus
- 1]);
1119 mi
= (pt_menuitem
) malloc(sizeof(t_menuitem
));
1122 m
->items
[(unsigned int)m
->numitems
] = mi
;
1123 mi
->handler
= NULL
; // No handler
1125 // Allocate space to store stuff
1126 mi
->item
= (char *)malloc(MENULEN
+ 1);
1127 mi
->status
= (char *)malloc(STATLEN
+ 1);
1128 mi
->data
= (char *)malloc(ACTIONLEN
+ 1);
1131 if (strlen(item
) > MENULEN
) {
1132 strcpy(mi
->item
, ITEMLONG
);
1134 strcpy(mi
->item
, item
);
1136 if (strlen(mi
->item
) > m
->menuwidth
)
1137 m
->menuwidth
= strlen(mi
->item
);
1139 strcpy(mi
->item
, EMPTYSTR
);
1142 if (strlen(status
) > STATLEN
) {
1143 strcpy(mi
->status
, STATUSLONG
);
1145 strcpy(mi
->status
, status
);
1148 strcpy(mi
->status
, EMPTYSTR
);
1150 mi
->action
= action
;
1153 mi
->helpid
= 0xFFFF;
1154 inhlite
= 0; // We have not yet seen an ENABLEHLITE char
1155 // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
1157 if (*str
== ENABLEHLITE
) {
1160 if (*str
== DISABLEHLITE
) {
1163 if ((inhlite
== 1) &&
1164 (((*str
>= 'A') && (*str
<= 'Z')) ||
1165 ((*str
>= 'a') && (*str
<= 'z')) ||
1166 ((*str
>= '0') && (*str
<= '9')))) {
1167 mi
->shortcut
= *str
;
1172 if ((mi
->shortcut
>= 'A') && (mi
->shortcut
<= 'Z')) // Make lower case
1173 mi
->shortcut
= mi
->shortcut
- 'A' + 'a';
1176 if (strlen(data
) > ACTIONLEN
) {
1177 strcpy(mi
->data
, ACTIONLONG
);
1179 strcpy(mi
->data
, data
);
1182 strcpy(mi
->data
, EMPTYSTR
);
1186 mi
->itemdata
.submenunum
= itemdata
;
1189 mi
->itemdata
.checked
= itemdata
;
1192 mi
->itemdata
.radiomenunum
= itemdata
;
1195 mi
->data
= NULL
; // No selection made
1197 default: // to keep the compiler happy
1200 mi
->index
= m
->numitems
++;
1201 mi
->parindex
= ms
->nummenus
- 1;
1205 // Set the shortcut key for the current item
1206 void set_item_options(uchar shortcut
, int helpid
)
1211 m
= (ms
->menus
[ms
->nummenus
- 1]);
1212 if (m
->numitems
<= 0)
1214 mi
= m
->items
[(unsigned int)m
->numitems
- 1];
1216 if (shortcut
!= 0xFF)
1217 mi
->shortcut
= shortcut
;
1218 if (helpid
!= 0xFFFF)
1219 mi
->helpid
= helpid
;
1222 // Free internal datasutructures
1223 void close_menusystem(void)
1227 // append_line_helper(pt_menu menu,char *line)
1228 void append_line_helper(int menunum
, char *line
)
1235 menu
= ms
->menus
[menunum
];
1236 for (ctr
= 0; ctr
< (int)menu
->numitems
; ctr
++) {
1237 mi
= menu
->items
[ctr
];
1238 app
= NULL
; //What to append
1239 switch (mi
->action
) {
1241 if (mi
->itemdata
.checked
)
1245 if (mi
->data
) { // Some selection has been made
1246 ri
= (pt_menuitem
) (mi
->data
);
1251 append_line_helper(mi
->itemdata
.submenunum
, line
);
1263 // Generate string based on state of checkboxes and radioitem in given menu
1264 // Assume line points to large enough buffer
1265 void gen_append_line(const char *menu_name
, char *line
)
1269 menunum
= find_menu_num(menu_name
);
1271 return; // No such menu
1272 append_line_helper(menunum
, line
);