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 * ----------------------------------------------------------------------- */
18 static pt_menusystem ms
; // Pointer to the menusystem
19 char TITLESTR
[] = "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
20 char TITLELONG
[] = " TITLE too long ";
21 char ITEMLONG
[] = " ITEM too long ";
22 char ACTIONLONG
[] = " ACTION too long ";
23 char STATUSLONG
[] = " STATUS too long ";
26 /* Forward declarations */
27 int calc_visible(pt_menu menu
,int first
);
28 int next_visible(pt_menu menu
,int index
);
29 int prev_visible(pt_menu menu
,int index
);
30 int next_visible_sep(pt_menu menu
,int index
);
31 int prev_visible_sep(pt_menu menu
,int index
);
32 int calc_first_early(pt_menu menu
,int curr
);
33 int calc_first_late(pt_menu menu
,int curr
);
34 int isvisible(pt_menu menu
,int first
, int curr
);
37 /* Basic Menu routines */
39 // This is same as inputc except it honors the ontimeout handler
40 // and calls it when needed. For the callee, there is no difference
41 // as this will not return unless a key has been pressed.
42 char getch(char *scan
)
48 // Wait until keypress if no handler specified
49 if ((ms
->ontimeout
==NULL
) && (ms
->ontotaltimeout
==NULL
)) return inputc(scan
);
52 while (1) // Forever do
54 for (i
=0; i
< ms
->tm_numsteps
; i
++)
56 if (checkkbdbuf()) return inputc(scan
);
57 sleep(ms
->tm_stepsize
);
58 if ( (ms
->tm_total_timeout
== 0) || (ms
->ontotaltimeout
==NULL
))
59 continue; // Dont bother with calculations if no handler
60 ms
->tm_sofar_timeout
+= ms
->tm_stepsize
;
61 if (ms
->tm_sofar_timeout
>= ms
->tm_total_timeout
) {
62 th
= ms
->ontotaltimeout
;
63 ms
->tm_sofar_timeout
= 0;
64 break; // Get out of the for loop
67 if (!th
) continue; // no handler dont call
71 case CODE_ENTER
: // Pretend user hit enter
73 return '\015'; // \015 octal = 13
74 case CODE_ESCAPE
: // Pretend user hit escape
76 return '\033'; // \033 octal = 27
84 /* Print a menu item */
85 /* attr[0] is non-hilite attr, attr[1] is highlight attr */
86 void printmenuitem(const char *str
,uchar
* attr
)
88 uchar page
= getdisppage();
90 int hlite
=NOHLITE
; // Initially no highlighting
92 getpos(&row
,&col
,page
);
105 case BELL
: // No Bell Char
107 case ENABLEHLITE
: // Switch on highlighting
110 case DISABLEHLITE
: // Turn off highlighting
114 putch(*str
, attr
[hlite
], page
);
117 if (col
> getnumcols())
122 if (row
> getnumrows())
127 gotoxy(row
,col
,page
);
132 int find_shortcut(pt_menu menu
,uchar shortcut
, int index
)
133 // Find the next index with specified shortcut key
138 // Garbage in garbage out
139 if ((index
<0) || (index
>= menu
->numitems
)) return index
;
141 // Go till end of menu
142 while (ans
< menu
->numitems
)
144 mi
= menu
->items
[ans
];
145 if ((mi
->action
== OPT_INVISIBLE
) || (mi
->action
== OPT_SEP
)
146 || (mi
->shortcut
!= shortcut
))
150 // Start at the beginning and try again
154 mi
= menu
->items
[ans
];
155 if ((mi
->action
== OPT_INVISIBLE
) || (mi
->action
== OPT_SEP
)
156 || (mi
->shortcut
!= shortcut
))
160 return index
; // Sorry not found
163 // print the menu starting from FIRST
164 // will print a maximum of menu->menuheight items
165 void printmenu(pt_menu menu
, int curr
, uchar top
, uchar left
, uchar first
)
167 int x
,row
; // x = index, row = position from top
168 int numitems
,menuwidth
;
169 char fchar
[5],lchar
[5]; // The first and last char in for each entry
170 const char *str
; // and inbetween the item or a seperator is printed
171 uchar
*attr
; // attribute attr
172 char sep
[MENULEN
];// and inbetween the item or a seperator is printed
175 numitems
= calc_visible(menu
,first
);
176 if (numitems
> menu
->menuheight
) numitems
= menu
->menuheight
;
178 menuwidth
= menu
->menuwidth
+3;
179 clearwindow(top
,left
-2, top
+numitems
+1, left
+menuwidth
+1,
180 ms
->menupage
, ms
->fillchar
, ms
->shadowattr
);
181 drawbox(top
-1,left
-3,top
+numitems
,left
+menuwidth
,
182 ms
->menupage
,ms
->normalattr
[NOHLITE
],ms
->menubt
);
183 memset(sep
,ms
->box_horiz
,menuwidth
); // String containing the seperator string
184 sep
[menuwidth
-1] = 0;
186 x
= (menuwidth
- strlen(menu
->title
) - 1) >> 1;
187 gotoxy(top
-1,left
+x
,ms
->menupage
);
188 printmenuitem(menu
->title
,ms
->normalattr
);
189 row
= -1; // 1 less than inital value of x
190 for (x
=first
; x
< menu
->numitems
; x
++)
193 if (ci
->action
== OPT_INVISIBLE
) continue;
195 if (row
>= numitems
) break; // Already have enough number of items
196 // Setup the defaults now
197 lchar
[0] = fchar
[0] = ' ';
198 lchar
[1] = fchar
[1] = '\0'; // fchar and lchar are just spaces
199 str
= ci
->item
; // Pointer to item string
200 attr
= (x
==curr
? ms
->reverseattr
: ms
->normalattr
); // Normal attributes
201 switch (ci
->action
) // set up attr,str,fchar,lchar for everything
204 attr
= (x
==curr
? ms
->revinactattr
: ms
->inactattr
);
207 lchar
[0] = SUBMENUCHAR
; lchar
[1] = 0;
210 lchar
[0] = RADIOMENUCHAR
; lchar
[1] = 0;
213 lchar
[0] = (ci
->itemdata
.checked
? CHECKED
: UNCHECKED
);
217 fchar
[0] = '\b'; fchar
[1] = ms
->box_ltrt
; fchar
[2] = ms
->box_horiz
; fchar
[3] = ms
->box_horiz
; fchar
[4] = 0;
218 lchar
[0] = ms
->box_horiz
; lchar
[1] = ms
->box_rtlt
; lchar
[2] = 0;
222 fchar
[0] = EXITMENUCHAR
; fchar
[1] = 0;
224 default: // Just to keep the compiler happy
227 gotoxy(top
+row
,left
-2,ms
->menupage
);
228 cprint(ms
->spacechar
,attr
[NOHLITE
],menuwidth
+2,ms
->menupage
); // Wipe area with spaces
229 gotoxy(top
+row
,left
-2,ms
->menupage
);
230 csprint(fchar
,attr
[NOHLITE
]); // Print first part
231 gotoxy(top
+row
,left
,ms
->menupage
);
232 printmenuitem(str
,attr
); // Print main part
233 gotoxy(top
+row
,left
+menuwidth
-1,ms
->menupage
); // Last char if any
234 csprint(lchar
,attr
[NOHLITE
]); // Print last part
236 // Check if we need to MOREABOVE and MOREBELOW to be added
239 x
= next_visible_sep(menu
,0); // First item
240 if (! isvisible(menu
,first
,x
)) // There is more above
243 gotoxy(top
,left
+menuwidth
,ms
->menupage
);
244 cprint(MOREABOVE
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
246 x
= prev_visible_sep(menu
,menu
->numitems
); // last item
247 if (! isvisible(menu
,first
,x
)) // There is more above
250 gotoxy(top
+numitems
-1,left
+menuwidth
,ms
->menupage
);
251 cprint(MOREBELOW
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
254 x
= ((numitems
-1)*curr
)/(menu
->numitems
);
255 if ((x
>0) && (row
==1)) {
256 gotoxy(top
+x
,left
+menuwidth
,ms
->menupage
);
257 cprint(SCROLLBOX
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
259 if (ms
->handler
) ms
->handler(ms
,menu
->items
[curr
]);
262 // Difference between this and regular menu, is that only
263 // OPT_INVISIBLE, OPT_SEP are honoured
264 void printradiomenu(pt_menu menu
, int curr
, uchar top
, uchar left
, int first
)
266 int x
,row
; // x = index, row = position from top
267 int numitems
,menuwidth
;
268 char fchar
[5],lchar
[5]; // The first and last char in for each entry
269 const char *str
; // and inbetween the item or a seperator is printed
270 uchar
*attr
; // all in the attribute attr
271 char sep
[MENULEN
];// and inbetween the item or a seperator is printed
274 numitems
= calc_visible(menu
,first
);
275 if (numitems
> menu
->menuheight
) numitems
= menu
->menuheight
;
277 menuwidth
= menu
->menuwidth
+3;
278 clearwindow(top
,left
-2, top
+numitems
+1, left
+menuwidth
+1,
279 ms
->menupage
, ms
->fillchar
, ms
->shadowattr
);
280 drawbox(top
-1,left
-3,top
+numitems
,left
+menuwidth
,
281 ms
->menupage
,ms
->normalattr
[NOHLITE
],ms
->menubt
);
282 memset(sep
,ms
->box_horiz
,menuwidth
); // String containing the seperator string
283 sep
[menuwidth
-1] = 0;
285 x
= (menuwidth
- strlen(menu
->title
) - 1) >> 1;
286 gotoxy(top
-1,left
+x
,ms
->menupage
);
287 printmenuitem(menu
->title
,ms
->normalattr
);
288 row
= -1; // 1 less than inital value of x
289 for (x
=first
; x
< menu
->numitems
; x
++)
292 if (ci
->action
== OPT_INVISIBLE
) continue;
294 if (row
> numitems
) break;
295 // Setup the defaults now
296 fchar
[0] = RADIOUNSEL
; fchar
[1]='\0'; // Unselected ( )
297 lchar
[0] = '\0'; // Nothing special after
298 str
= ci
->item
; // Pointer to item string
299 attr
= ms
->normalattr
; // Always same attribute
300 fchar
[0] = (x
==curr
? RADIOSEL
: RADIOUNSEL
);
301 switch (ci
->action
) // set up attr,str,fchar,lchar for everything
304 attr
= ms
->inactattr
;
307 fchar
[0] = '\b'; fchar
[1] = ms
->box_ltrt
; fchar
[2] = ms
->box_horiz
; fchar
[3] = ms
->box_horiz
; fchar
[4] = 0;
308 lchar
[0] = ms
->box_horiz
; lchar
[1] = ms
->box_rtlt
; lchar
[3] = 0;
311 default: // To keep the compiler happy
314 gotoxy(top
+row
,left
-2,ms
->menupage
);
315 cprint(ms
->spacechar
,attr
[NOHLITE
],menuwidth
+2,ms
->menupage
); // Wipe area with spaces
316 gotoxy(top
+row
,left
-2,ms
->menupage
);
317 csprint(fchar
,attr
[NOHLITE
]); // Print first part
318 gotoxy(top
+row
,left
,ms
->menupage
);
319 printmenuitem(str
,attr
); // Print main part
320 gotoxy(top
+row
,left
+menuwidth
-1,ms
->menupage
); // Last char if any
321 csprint(lchar
,attr
[NOHLITE
]); // Print last part
323 // Check if we need to MOREABOVE and MOREBELOW to be added
326 x
= next_visible_sep(menu
,0); // First item
327 if (! isvisible(menu
,first
,x
)) // There is more above
330 gotoxy(top
,left
+menuwidth
,ms
->menupage
);
331 cprint(MOREABOVE
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
333 x
= prev_visible_sep(menu
,menu
->numitems
); // last item
334 if (! isvisible(menu
,first
,x
)) // There is more above
337 gotoxy(top
+numitems
-1,left
+menuwidth
,ms
->menupage
);
338 cprint(MOREBELOW
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
341 x
= ((numitems
-1)*curr
)/(menu
->numitems
);
342 if ((x
> 0) && (row
== 1))
344 gotoxy(top
+x
,left
+menuwidth
,ms
->menupage
);
345 cprint(SCROLLBOX
,ms
->normalattr
[NOHLITE
],1,ms
->menupage
);
347 if (ms
->handler
) ms
->handler(ms
,menu
->items
[curr
]);
350 void cleanupmenu(pt_menu menu
, uchar top
,uchar left
,int numitems
)
352 if (numitems
> menu
->menuheight
) numitems
= menu
->menuheight
;
353 clearwindow(top
,left
-2, top
+numitems
+1, left
+menu
->menuwidth
+4,
354 ms
->menupage
, ms
->fillchar
, ms
->fillattr
); // Clear the shadow
355 clearwindow(top
-1, left
-3, top
+numitems
, left
+menu
->menuwidth
+3,
356 ms
->menupage
, ms
->fillchar
, ms
->fillattr
); // main window
359 /* Handle a radio menu */
360 pt_menuitem
getradiooption(pt_menu menu
, uchar top
, uchar left
, uchar startopt
)
361 // Return item chosen or NULL if ESC was hit.
363 int curr
,i
,first
,tmp
;
366 pt_menuitem ci
; // Current item
368 numitems
= calc_visible(menu
,0);
370 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
371 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,ms
->menupage
);
373 // Initialise current menu item
374 curr
= next_visible(menu
,startopt
);
376 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
377 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,1);
378 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
379 printmenuitem(menu
->items
[curr
]->status
,ms
->statusattr
);
380 first
= calc_first_early(menu
,curr
);
383 printradiomenu(menu
,curr
,top
,left
,first
);
384 ci
= menu
->items
[curr
];
390 curr
= next_visible(menu
,0);
391 first
= calc_first_early(menu
,curr
);
394 curr
= prev_visible(menu
,numitems
-1);
395 first
= calc_first_late(menu
,curr
);
398 for (i
=0; i
< 5; i
++) curr
= next_visible(menu
,curr
+1);
399 first
= calc_first_late(menu
,curr
);
402 for (i
=0; i
< 5; i
++) curr
= prev_visible(menu
,curr
-1);
403 first
= calc_first_early(menu
,curr
);
406 curr
= prev_visible(menu
,curr
-1);
407 if (curr
< first
) first
= calc_first_early(menu
,curr
);
410 curr
= next_visible(menu
,curr
+1);
411 if (! isvisible(menu
,first
,curr
))
412 first
= calc_first_late(menu
,curr
);
421 if (ci
->action
== OPT_INACTIVE
) break;
422 if (ci
->action
== OPT_SEP
) break;
426 // Check if this is a shortcut key
427 if (((asc
>= 'A') && (asc
<= 'Z')) ||
428 ((asc
>= 'a') && (asc
<= 'z')) ||
429 ((asc
>= '0') && (asc
<= '9')))
431 tmp
= find_shortcut(menu
,asc
,curr
);
432 if ((tmp
> curr
) && (! isvisible(menu
,first
,tmp
)))
433 first
= calc_first_late(menu
,tmp
);
435 first
= calc_first_early(menu
,tmp
);
439 if (ms
->keys_handler
) // Call extra keys handler
440 ms
->keys_handler(ms
,menu
->items
[curr
],(scan
<< 8) | asc
);
444 // Update status line
445 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
446 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,ms
->menupage
);
447 printmenuitem(menu
->items
[curr
]->status
,ms
->statusattr
);
449 return NULL
; // Should never come here
452 /* Handle one menu */
453 pt_menuitem
getmenuoption(pt_menu menu
, uchar top
, uchar left
, uchar startopt
)
454 // Return item chosen or NULL if ESC was hit.
456 int curr
,i
,first
,tmp
;
459 pt_menuitem ci
; // Current item
460 t_handler_return hr
; // Return value of handler
462 numitems
= calc_visible(menu
,0);
464 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
465 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,ms
->menupage
);
467 // Initialise current menu item
468 curr
= next_visible(menu
,startopt
);
470 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
471 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,1);
472 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
473 printmenuitem(menu
->items
[curr
]->status
,ms
->statusattr
);
474 first
= calc_first_early(menu
,curr
);
477 printmenu(menu
,curr
,top
,left
,first
);
478 ci
= menu
->items
[curr
];
483 curr
= next_visible(menu
,0);
484 first
= calc_first_early(menu
,curr
);
487 curr
= prev_visible(menu
,numitems
-1);
488 first
= calc_first_late(menu
,curr
);
491 for (i
=0; i
< 5; i
++) curr
= next_visible(menu
,curr
+1);
492 first
= calc_first_late(menu
,curr
);
495 for (i
=0; i
< 5; i
++) curr
= prev_visible(menu
,curr
-1);
496 first
= calc_first_early(menu
,curr
);
499 curr
= prev_visible(menu
,curr
-1);
500 if (curr
< first
) first
= calc_first_early(menu
,curr
);
503 curr
= next_visible(menu
,curr
+1);
504 if (! isvisible(menu
,first
,curr
))
505 first
= calc_first_late(menu
,curr
);
514 if (ci
->action
== OPT_INACTIVE
) break;
515 if (ci
->action
== OPT_CHECKBOX
) break;
516 if (ci
->action
== OPT_SEP
) break;
517 if (ci
->action
== OPT_EXITMENU
) return NULL
; // As if we hit Esc
518 // If we are going into a radio menu, dont call handler, return ci
519 if (ci
->action
== OPT_RADIOMENU
) return ci
;
520 if (ci
->handler
!= NULL
) // Do we have a handler
522 hr
= ci
->handler(ms
,ci
);
523 if (hr
.refresh
) // Do we need to refresh
525 // Cleanup menu using old number of items
526 cleanupmenu(menu
,top
,left
,numitems
);
527 // Recalculate the number of items
528 numitems
= calc_visible(menu
,0);
530 printmenu(menu
,curr
,top
,left
,first
);
532 if (hr
.valid
) return ci
;
537 if (ci
->action
!= OPT_CHECKBOX
) break;
538 ci
->itemdata
.checked
= !ci
->itemdata
.checked
;
539 if (ci
->handler
!= NULL
) // Do we have a handler
541 hr
= ci
->handler(ms
,ci
);
542 if (hr
.refresh
) // Do we need to refresh
544 // Cleanup menu using old number of items
545 cleanupmenu(menu
,top
,left
,numitems
);
546 // Recalculate the number of items
547 numitems
= calc_visible(menu
,0);
549 printmenu(menu
,curr
,top
,left
,first
);
554 // Check if this is a shortcut key
555 if (((asc
>= 'A') && (asc
<= 'Z')) ||
556 ((asc
>= 'a') && (asc
<= 'z')) ||
557 ((asc
>= '0') && (asc
<= '9')))
559 tmp
= find_shortcut(menu
,asc
,curr
);
560 if ((tmp
> curr
) && (! isvisible(menu
,first
,tmp
)))
561 first
= calc_first_late(menu
,tmp
);
563 first
= calc_first_early(menu
,tmp
);
567 if (ms
->keys_handler
) // Call extra keys handler
568 ms
->keys_handler(ms
,menu
->items
[curr
],(scan
<< 8) | asc
);
572 // Update status line
573 gotoxy(ms
->minrow
+ms
->statline
,ms
->mincol
,ms
->menupage
);
574 cprint(ms
->spacechar
,ms
->statusattr
[NOHLITE
],ms
->numcols
,ms
->menupage
);
575 printmenuitem(menu
->items
[curr
]->status
,ms
->statusattr
);
577 return NULL
; // Should never come here
580 /* Handle the entire system of menu's. */
581 pt_menuitem
runmenusystem(uchar top
, uchar left
, pt_menu cmenu
, uchar startopt
, uchar menutype
)
584 * Which menu should be currently displayed
586 * What is the position of the top,left corner of the menu
588 * which menu item do I start with
590 * NORMALMENU or RADIOMENU
593 * Returns a pointer to the final item chosen, or NULL if nothing chosen.
596 pt_menuitem opt
,choice
;
600 if (cmenu
== NULL
) return NULL
;
602 // Set the menu height
603 cmenu
->menuheight
= ms
->maxrow
- top
-3;
604 if (cmenu
->menuheight
> ms
->maxmenuheight
)
605 cmenu
->menuheight
= ms
->maxmenuheight
;
606 if (menutype
== NORMALMENU
)
607 opt
= getmenuoption(cmenu
,top
,left
,startopt
);
608 else // menutype == RADIOMENU
609 opt
= getradiooption(cmenu
,top
,left
,startopt
);
614 cleanupmenu(cmenu
,top
,left
,calc_visible(cmenu
,0));
617 // Are we done with the menu system?
618 if ((opt
->action
!= OPT_SUBMENU
) && (opt
->action
!= OPT_RADIOMENU
))
620 cleanupmenu(cmenu
,top
,left
,calc_visible(cmenu
,0));
621 return opt
; // parent cleanup other menus
623 // Either radiomenu or submenu
624 // Do we have a valid menu number? The next hack uses the fact that
625 // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
626 if (opt
->itemdata
.submenunum
>= ms
->nummenus
) // This is Bad....
628 gotoxy(12,12,ms
->menupage
); // Middle of screen
629 csprint("ERROR: Invalid submenu requested.",0x07);
630 cleanupmenu(cmenu
,top
,left
,calc_visible(cmenu
,0));
631 return NULL
; // Pretend user hit esc
633 // Call recursively for submenu
634 // Position the submenu below the current item,
635 // covering half the current window (horizontally)
636 row
= ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
]->row
;
637 col
= ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
]->col
;
638 if (row
== 0xFF) row
= top
+opt
->index
+2;
639 if (col
== 0xFF) col
= left
+3+(cmenu
->menuwidth
>> 1);
640 mt
= (opt
->action
== OPT_SUBMENU
? NORMALMENU
: RADIOMENU
);
642 if ((opt
->action
== OPT_RADIOMENU
) && (opt
->data
!= NULL
))
643 startat
= ((t_menuitem
*)opt
->data
)->index
;
645 choice
= runmenusystem(row
, col
,
646 ms
->menus
[(unsigned int)opt
->itemdata
.submenunum
],
648 if (opt
->action
== OPT_RADIOMENU
)
650 if (choice
!= NULL
) opt
->data
= (void *)choice
; // store choice in data field
651 if (opt
->handler
!= NULL
) opt
->handler(ms
,opt
);
652 choice
= NULL
; // Pretend user hit esc
654 if (choice
==NULL
) // User hit Esc in submenu
657 startopt
= opt
->index
;
662 cleanupmenu(cmenu
,top
,left
,calc_visible(cmenu
,0));
667 // Finds the indexof the menu with given name
668 uchar
find_menu_num(const char *name
)
673 if (name
== NULL
) return (uchar
)(-1);
674 for (i
=0; i
< ms
->nummenus
; i
++)
677 if ((m
->name
) && (strcmp(m
->name
,name
)==0)) return i
;
682 // Run through all items and if they are submenus
683 // with a non-trivial "action" and trivial submenunum
684 // replace submenunum with the menu with name "action"
692 for (i
=0; i
< ms
->nummenus
; i
++)
695 for (j
=0; j
< m
->numitems
; j
++)
698 // if submenu with non-trivial data string
699 // again using hack that itemdata is a union data type
700 if ( mi
->data
&& ((mi
->action
== OPT_SUBMENU
) || (mi
->action
== OPT_RADIOMENU
)) )
701 mi
->itemdata
.submenunum
= find_menu_num (mi
->data
);
706 /* User Callable functions */
708 pt_menuitem
showmenus(uchar startmenu
)
713 fix_submenus(); // Fix submenu numbers incase nick names were used
715 // Setup screen for menusystem
716 oldpage
= getdisppage();
717 setdisppage(ms
->menupage
);
719 clearwindow(ms
->minrow
, ms
->mincol
, ms
->maxrow
, ms
->maxcol
,
720 ms
->menupage
, ms
->fillchar
, ms
->fillattr
);
721 tpos
= (ms
->numcols
- strlen(ms
->title
) - 1) >> 1; // center it on line
722 gotoxy(ms
->minrow
,ms
->mincol
,ms
->menupage
);
723 cprint(ms
->tfillchar
,ms
->titleattr
,ms
->numcols
,ms
->menupage
);
724 gotoxy(ms
->minrow
,ms
->mincol
+tpos
,ms
->menupage
);
725 csprint(ms
->title
,ms
->titleattr
);
727 cursoroff(); // Doesn't seem to work?
730 // Go, main menu cannot be a radio menu
731 rv
= runmenusystem(ms
->minrow
+MENUROW
, ms
->mincol
+MENUCOL
,
732 ms
->menus
[(unsigned int)startmenu
], 0, NORMALMENU
);
734 // Hide the garbage we left on the screen
736 if (oldpage
== ms
->menupage
) cls(); else setdisppage(oldpage
);
738 // Return user choice
742 pt_menusystem
init_menusystem(const char *title
)
747 ms
= (pt_menusystem
) malloc(sizeof(t_menusystem
));
748 if (ms
== NULL
) return NULL
;
750 // Initialise all menu pointers
751 for (i
=0; i
< MAXMENUS
; i
++) ms
->menus
[i
] = NULL
;
753 ms
->title
= (char *)malloc(TITLELEN
+1);
755 strcpy(ms
->title
,TITLESTR
); // Copy string
756 else strcpy(ms
->title
,title
);
759 ms
->tm_stepsize
= TIMEOUTSTEPSIZE
;
760 ms
->tm_numsteps
= TIMEOUTNUMSTEPS
;
762 ms
->normalattr
[NOHLITE
] = NORMALATTR
;
763 ms
->normalattr
[HLITE
] = NORMALHLITE
;
765 ms
->reverseattr
[NOHLITE
] = REVERSEATTR
;
766 ms
->reverseattr
[HLITE
] = REVERSEHLITE
;
768 ms
->inactattr
[NOHLITE
] = INACTATTR
;
769 ms
->inactattr
[HLITE
] = INACTHLITE
;
771 ms
->revinactattr
[NOHLITE
] = REVINACTATTR
;
772 ms
->revinactattr
[HLITE
] = REVINACTHLITE
;
774 ms
->statusattr
[NOHLITE
] = STATUSATTR
;
775 ms
->statusattr
[HLITE
] = STATUSHLITE
;
777 ms
->statline
= STATLINE
;
778 ms
->tfillchar
= TFILLCHAR
;
779 ms
->titleattr
= TITLEATTR
;
781 ms
->fillchar
= FILLCHAR
;
782 ms
->fillattr
= FILLATTR
;
783 ms
->spacechar
= SPACECHAR
;
784 ms
->shadowattr
= SHADOWATTR
;
786 ms
->menupage
= MENUPAGE
; // Usually no need to change this at all
788 // Initialise all handlers
790 ms
->keys_handler
= NULL
;
791 ms
->ontimeout
=NULL
; // No timeout handler
792 ms
->tm_total_timeout
= 0;
793 ms
->tm_sofar_timeout
= 0;
794 ms
->ontotaltimeout
= NULL
;
796 // Setup ACTION_{,IN}VALID
797 ACTION_VALID
.valid
=1;
798 ACTION_VALID
.refresh
=0;
799 ACTION_INVALID
.valid
= 0;
800 ACTION_INVALID
.refresh
= 0;
802 // Figure out the size of the screen we are in now.
803 // By default we use the whole screen for our menu
804 ms
->minrow
= ms
->mincol
= 0;
805 ms
->numcols
= getnumcols();
806 ms
->numrows
= getnumrows();
807 ms
->maxcol
= ms
->numcols
- 1;
808 ms
->maxrow
= ms
->numrows
- 1;
810 // How many entries per menu can we display at a time
811 ms
->maxmenuheight
= ms
->maxrow
- ms
->minrow
- 3;
812 if (ms
->maxmenuheight
> MAXMENUHEIGHT
)
813 ms
->maxmenuheight
= MAXMENUHEIGHT
;
815 // Set up the look of the box
816 set_box_type(MENUBOXTYPE
);
820 void set_normal_attr(uchar normal
, uchar selected
, uchar inactivenormal
, uchar inactiveselected
)
822 if (normal
!= 0xFF) ms
->normalattr
[0] = normal
;
823 if (selected
!= 0xFF) ms
->reverseattr
[0] = selected
;
824 if (inactivenormal
!= 0xFF) ms
->inactattr
[0] = inactivenormal
;
825 if (inactiveselected
!= 0xFF) ms
->revinactattr
[0] = inactiveselected
;
828 void set_normal_hlite(uchar normal
, uchar selected
, uchar inactivenormal
, uchar inactiveselected
)
830 if (normal
!= 0xFF) ms
->normalattr
[1] = normal
;
831 if (selected
!= 0xFF) ms
->reverseattr
[1] = selected
;
832 if (inactivenormal
!= 0xFF) ms
->inactattr
[1] = inactivenormal
;
833 if (inactiveselected
!= 0xFF) ms
->revinactattr
[1] = inactiveselected
;
836 void set_status_info(uchar statusattr
, uchar statushlite
, uchar statline
)
838 if (statusattr
!= 0xFF) ms
->statusattr
[NOHLITE
] = statusattr
;
839 if (statushlite
!= 0xFF) ms
->statusattr
[HLITE
] = statushlite
;
840 // statline is relative to minrow
841 if (statline
>= ms
->numrows
) statline
= ms
->numrows
- 1;
842 ms
->statline
= statline
; // relative to ms->minrow, 0 based
845 void set_title_info(uchar tfillchar
, uchar titleattr
)
847 if (tfillchar
!= 0xFF) ms
->tfillchar
= tfillchar
;
848 if (titleattr
!= 0xFF) ms
->titleattr
= titleattr
;
851 void set_misc_info(uchar fillchar
, uchar fillattr
,uchar spacechar
, uchar shadowattr
)
853 if (fillchar
!= 0xFF) ms
->fillchar
= fillchar
;
854 if (fillattr
!= 0xFF) ms
->fillattr
= fillattr
;
855 if (spacechar
!= 0xFF) ms
->spacechar
= spacechar
;
856 if (shadowattr
!= 0xFF) ms
->shadowattr
= shadowattr
;
859 void set_box_type(boxtype bt
)
863 bxc
= getboxchars(bt
);
864 ms
->box_horiz
= bxc
[BOX_HORIZ
]; // The char used to draw top line
865 ms
->box_ltrt
= bxc
[BOX_LTRT
];
866 ms
->box_rtlt
= bxc
[BOX_RTLT
];
869 void set_menu_options(uchar maxmenuheight
)
871 if (maxmenuheight
!= 0xFF) ms
->maxmenuheight
= maxmenuheight
;
874 // Set the window which menusystem should use
875 void set_window_size(uchar top
, uchar left
, uchar bot
, uchar right
)
879 if ((top
> bot
) || (left
> right
)) return; // Sorry no change will happen here
882 if (bot
>= nr
) bot
= nr
-1;
883 if (right
>= nc
) right
= nc
-1;
888 ms
->numcols
= right
- left
+ 1;
889 ms
->numrows
= bot
- top
+ 1;
890 if (ms
->statline
>= ms
->numrows
) ms
->statline
= ms
->numrows
- 1; // Clip statline if need be
893 void reg_handler( t_handler htype
, void * handler
)
895 // If bad value set to default screen handler
898 ms
->keys_handler
= (t_keys_handler
) handler
;
901 ms
->handler
= (t_menusystem_handler
) handler
;
906 void unreg_handler(t_handler htype
)
910 ms
->keys_handler
= NULL
;
918 void reg_ontimeout(t_timeout_handler handler
, unsigned int numsteps
, unsigned int stepsize
)
920 ms
->ontimeout
= handler
;
921 if (numsteps
!= 0) ms
->tm_numsteps
= numsteps
;
922 if (stepsize
!= 0) ms
->tm_stepsize
= stepsize
;
925 void unreg_ontimeout()
927 ms
->ontimeout
= NULL
;
930 void reg_ontotaltimeout (t_timeout_handler handler
, unsigned long numcentiseconds
)
932 if (numcentiseconds
!= 0) {
933 ms
->ontotaltimeout
= handler
;
934 ms
->tm_total_timeout
= numcentiseconds
*10; // to convert to milliseconds
935 ms
->tm_sofar_timeout
= 0;
939 void unreg_ontotaltimeout()
941 ms
->ontotaltimeout
= NULL
;
945 int next_visible(pt_menu menu
, int index
)
948 if (index
< 0) ans
= 0 ;
949 else if (index
>= menu
->numitems
) ans
= menu
->numitems
-1;
951 while ((ans
< menu
->numitems
-1) &&
952 ((menu
->items
[ans
]->action
== OPT_INVISIBLE
) ||
953 (menu
->items
[ans
]->action
== OPT_SEP
)))
958 int prev_visible(pt_menu menu
, int index
) // Return index of prev visible
961 if (index
< 0) ans
= 0;
962 else if (index
>= menu
->numitems
) ans
= menu
->numitems
-1;
965 ((menu
->items
[ans
]->action
== OPT_INVISIBLE
) ||
966 (menu
->items
[ans
]->action
== OPT_SEP
)))
971 int next_visible_sep(pt_menu menu
, int index
)
974 if (index
< 0) ans
= 0 ;
975 else if (index
>= menu
->numitems
) ans
= menu
->numitems
-1;
977 while ((ans
< menu
->numitems
-1) &&
978 (menu
->items
[ans
]->action
== OPT_INVISIBLE
))
983 int prev_visible_sep(pt_menu menu
, int index
) // Return index of prev visible
986 if (index
< 0) ans
= 0;
987 else if (index
>= menu
->numitems
) ans
= menu
->numitems
-1;
990 (menu
->items
[ans
]->action
== OPT_INVISIBLE
))
995 int calc_visible(pt_menu menu
,int first
)
999 if (menu
== NULL
) return 0;
1001 for (i
=first
; i
< menu
->numitems
; i
++)
1002 if (menu
->items
[i
]->action
!= OPT_INVISIBLE
) ans
++;
1006 // is curr visible if first entry is first?
1007 int isvisible(pt_menu menu
,int first
, int curr
)
1009 if (curr
< first
) return 0;
1010 return (calc_visible(menu
,first
)-calc_visible(menu
,curr
) < menu
->menuheight
);
1013 // Calculate the first entry to be displayed
1014 // so that curr is visible and make curr as late as possible
1015 int calc_first_late(pt_menu menu
,int curr
)
1019 nv
= calc_visible(menu
,0);
1020 if (nv
<= menu
->menuheight
) return 0;
1021 // Start with curr and go back menu->menuheight times
1023 for (i
=0; i
< menu
->menuheight
; i
++)
1024 ans
= prev_visible_sep(menu
,ans
-1);
1028 // Calculate the first entry to be displayed
1029 // so that curr is visible and make curr as early as possible
1030 int calc_first_early(pt_menu menu
,int curr
)
1034 nv
= calc_visible(menu
,0);
1035 if (nv
<= menu
->menuheight
) return 0;
1036 // Start with curr and go back till >= menu->menuheight
1037 // items are visible
1038 nv
= calc_visible(menu
,curr
); // Already nv of them are visible
1040 for (i
=0; i
< menu
->menuheight
- nv
; i
++)
1041 ans
= prev_visible_sep(menu
,ans
-1);
1045 // Create a new menu and return its position
1046 uchar
add_menu(const char *title
, int maxmenusize
)
1052 if (num
>= MAXMENUS
) return -1;
1054 m
= (pt_menu
) malloc(sizeof(t_menu
));
1055 if (m
== NULL
) return -1;
1061 if (maxmenusize
< 1)
1062 m
->maxmenusize
= MAXMENUSIZE
;
1063 else m
->maxmenusize
= maxmenusize
;
1064 m
->items
= (pt_menuitem
*) malloc(sizeof(pt_menuitem
)*(m
->maxmenusize
));
1065 for (i
=0; i
< m
->maxmenusize
; i
++) m
->items
[i
] = NULL
;
1067 m
->title
= (char *)malloc(MENULEN
+1);
1070 if (strlen(title
) > MENULEN
- 2)
1071 strcpy(m
->title
,TITLELONG
);
1072 else strcpy(m
->title
,title
);
1074 else strcpy(m
->title
,EMPTYSTR
);
1075 m
->menuwidth
= strlen(m
->title
);
1077 return ms
->nummenus
- 1;
1080 void set_menu_name(const char *name
) // Set the "name" of this menu
1084 m
= ms
->menus
[ms
->nummenus
-1];
1085 if (m
->name
) // Free up previous name
1093 m
->name
= (char *)malloc(strlen(name
)+1);
1094 strcpy(m
->name
,name
);
1098 // Create a new named menu and return its position
1099 uchar
add_named_menu(const char * name
, const char *title
, int maxmenusize
)
1101 add_menu(title
,maxmenusize
);
1102 set_menu_name(name
);
1103 return ms
->nummenus
- 1;
1106 void set_menu_pos(uchar row
,uchar col
) // Set the position of this menu.
1110 m
= ms
->menus
[ms
->nummenus
-1];
1115 pt_menuitem
add_sep() // Add a separator to current menu
1120 m
= (ms
->menus
[ms
->nummenus
-1]);
1122 mi
= (pt_menuitem
) malloc(sizeof(t_menuitem
));
1123 if (mi
== NULL
) return NULL
;
1124 m
->items
[(unsigned int)m
->numitems
] = mi
;
1125 mi
->handler
= NULL
; // No handler
1126 mi
->item
= mi
->status
= mi
->data
= NULL
;
1127 mi
->action
= OPT_SEP
;
1128 mi
->index
= m
->numitems
++;
1129 mi
->parindex
= ms
->nummenus
-1;
1135 // Add item to the "current" menu
1136 pt_menuitem
add_item(const char *item
, const char *status
, t_action action
,
1137 const char *data
, uchar itemdata
)
1142 uchar inhlite
=0; // Are we inside hlite area
1144 m
= (ms
->menus
[ms
->nummenus
-1]);
1146 mi
= (pt_menuitem
) malloc(sizeof(t_menuitem
));
1147 if (mi
== NULL
) return NULL
;
1148 m
->items
[(unsigned int) m
->numitems
] = mi
;
1149 mi
->handler
= NULL
; // No handler
1151 // Allocate space to store stuff
1152 mi
->item
= (char *)malloc(MENULEN
+1);
1153 mi
->status
= (char *)malloc(STATLEN
+1);
1154 mi
->data
= (char *)malloc(ACTIONLEN
+1);
1157 if (strlen(item
) > MENULEN
) {
1158 strcpy(mi
->item
,ITEMLONG
);
1160 strcpy(mi
->item
,item
);
1162 if (strlen(mi
->item
) > m
->menuwidth
) m
->menuwidth
= strlen(mi
->item
);
1163 } else strcpy(mi
->item
,EMPTYSTR
);
1166 if (strlen(status
) > STATLEN
) {
1167 strcpy(mi
->status
,STATUSLONG
);
1169 strcpy(mi
->status
,status
);
1171 } else strcpy(mi
->status
,EMPTYSTR
);
1176 mi
->helpid
= 0xFFFF;
1177 inhlite
= 0; // We have not yet seen an ENABLEHLITE char
1178 // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
1181 if (*str
== ENABLEHLITE
)
1185 if (*str
== DISABLEHLITE
)
1189 if ( (inhlite
== 1) &&
1190 (((*str
>= 'A') && (*str
<= 'Z')) ||
1191 ((*str
>= 'a') && (*str
<= 'z')) ||
1192 ((*str
>= '0') && (*str
<= '9'))))
1199 if ((mi
->shortcut
>= 'A') && (mi
->shortcut
<= 'Z')) // Make lower case
1200 mi
->shortcut
= mi
->shortcut
-'A'+'a';
1203 if (strlen(data
) > ACTIONLEN
) {
1204 strcpy(mi
->data
,ACTIONLONG
);
1206 strcpy(mi
->data
,data
);
1208 } else strcpy(mi
->data
,EMPTYSTR
);
1213 mi
->itemdata
.submenunum
= itemdata
;
1216 mi
->itemdata
.checked
= itemdata
;
1219 mi
->itemdata
.radiomenunum
= itemdata
;
1220 if (mi
->data
) free(mi
->data
);
1221 mi
->data
= NULL
; // No selection made
1223 default: // to keep the compiler happy
1226 mi
->index
= m
->numitems
++;
1227 mi
->parindex
= ms
->nummenus
-1;
1231 // Set the shortcut key for the current item
1232 void set_item_options(uchar shortcut
,int helpid
)
1237 m
= (ms
->menus
[ms
->nummenus
-1]);
1238 if (m
->numitems
<= 0) return;
1239 mi
= m
->items
[(unsigned int) m
->numitems
-1];
1241 if (shortcut
!= 0xFF) mi
->shortcut
= shortcut
;
1242 if (helpid
!= 0xFFFF) mi
->helpid
= helpid
;
1245 // Free internal datasutructures
1246 void close_menusystem(void)
1250 // append_line_helper(pt_menu menu,char *line)
1251 void append_line_helper(int menunum
, char *line
)
1261 menu
= ms
->menus
[menunum
];
1262 for (ctr
= 0; ctr
< (int) menu
->numitems
; ctr
++)
1264 mi
= menu
->items
[ctr
];
1265 app
= NULL
; //What to append
1266 switch (mi
->action
) {
1268 if (mi
->itemdata
.checked
) app
= mi
->data
;
1271 if (mi
->data
) { // Some selection has been made
1272 ri
= (pt_menuitem
) (mi
->data
);
1277 append_line_helper(mi
->itemdata
.submenunum
,line
);
1290 // Generate string based on state of checkboxes and radioitem in given menu
1291 // Assume line points to large enough buffer
1292 void gen_append_line(const char *menu_name
,char *line
)
1296 menunum
= find_menu_num(menu_name
);
1297 if (menunum
< 0) return; // No such menu
1298 append_line_helper(menunum
,line
);