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 * ----------------------------------------------------------------------- */
17 static pt_menusystem ms; // Pointer to the menusystem
18 char TITLESTR[] = "COMBOOT Menu System for SYSLINUX developed by Murali Krishnan Ganapathy";
19 char TITLELONG[] = " TITLE too long ";
20 char ITEMLONG[] = " ITEM too long ";
21 char ACTIONLONG[] = " ACTION too long ";
22 char STATUSLONG[] = " STATUS too long ";
25 /* Forward declarations */
26 int calc_visible(pt_menu menu,int first);
27 int next_visible(pt_menu menu,int index);
28 int prev_visible(pt_menu menu,int index);
29 int next_visible_sep(pt_menu menu,int index);
30 int prev_visible_sep(pt_menu menu,int index);
31 int calc_first_early(pt_menu menu,int curr);
32 int calc_first_late(pt_menu menu,int curr);
33 int isvisible(pt_menu menu,int first, int curr);
36 /* Basic Menu routines */
38 // This is same as inputc except it honors the ontimeout handler
39 // and calls it when needed. For the callee, there is no difference
40 // as this will not return unless a key has been pressed.
41 char getch(char *scan)
46 // Wait until keypress if no handler specified
47 if (ms->ontimeout==NULL) return inputc(scan);
49 while (1) // Forever do
51 for (i=0; i < ms->tm_numsteps; i++)
53 if (checkkbdbuf()) return inputc(scan);
54 sleep(ms->tm_stepsize);
59 case CODE_ENTER: // Pretend user hit enter
61 return '\015'; // \015 octal = 13
62 case CODE_ESCAPE: // Pretend user hit escape
64 return '\033'; // \033 octal = 27
72 /* Print a menu item */
73 /* attr[0] is non-hilite attr, attr[1] is highlight attr */
74 void printmenuitem(const char *str,uchar* attr)
76 uchar page = getdisppage();
78 int hlite=NOHLITE; // Initially no highlighting
80 getpos(&row,&col,page);
93 case BELL: // No Bell Char
95 case ENABLEHLITE: // Switch on highlighting
98 case DISABLEHLITE: // Turn off highlighting
102 putch(*str, attr[hlite], page);
105 if (col > getnumcols())
110 if (row > getnumrows())
115 gotoxy(row,col,page);
120 int find_shortcut(pt_menu menu,uchar shortcut, int index)
121 // Find the next index with specified shortcut key
126 // Garbage in garbage out
127 if ((index <0) || (index >= menu->numitems)) return index;
129 // Go till end of menu
130 while (ans < menu->numitems)
132 mi = menu->items[ans];
133 if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
134 || (mi->shortcut != shortcut))
138 // Start at the beginning and try again
142 mi = menu->items[ans];
143 if ((mi->action == OPT_INVISIBLE) || (mi->action == OPT_SEP)
144 || (mi->shortcut != shortcut))
148 return index; // Sorry not found
151 // print the menu starting from FIRST
152 // will print a maximum of menu->menuheight items
153 void printmenu(pt_menu menu, int curr, uchar top, uchar left, uchar first)
155 int x,row; // x = index, row = position from top
156 int numitems,menuwidth;
157 char fchar[5],lchar[5]; // The first and last char in for each entry
158 const char *str; // and inbetween the item or a seperator is printed
159 uchar *attr; // attribute attr
160 char sep[MENULEN];// and inbetween the item or a seperator is printed
163 numitems = calc_visible(menu,first);
164 if (numitems > menu->menuheight) numitems = menu->menuheight;
166 menuwidth = menu->menuwidth+3;
167 clearwindow(top,left-2, top+numitems+1, left+menuwidth+1,
168 ms->menupage, ms->fillchar, ms->shadowattr);
169 drawbox(top-1,left-3,top+numitems,left+menuwidth,
170 ms->menupage,ms->normalattr[NOHLITE],ms->menubt);
171 memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string
172 sep[menuwidth-1] = 0;
174 x = (menuwidth - strlen(menu->title) - 1) >> 1;
175 gotoxy(top-1,left+x,ms->menupage);
176 printmenuitem(menu->title,ms->normalattr);
177 row = -1; // 1 less than inital value of x
178 for (x=first; x < menu->numitems; x++)
181 if (ci->action == OPT_INVISIBLE) continue;
183 if (row >= numitems) break; // Already have enough number of items
184 // Setup the defaults now
185 lchar[0] = fchar[0] = ' ';
186 lchar[1] = fchar[1] = '\0'; // fchar and lchar are just spaces
187 str = ci->item; // Pointer to item string
188 attr = (x==curr ? ms->reverseattr : ms->normalattr); // Normal attributes
189 switch (ci->action) // set up attr,str,fchar,lchar for everything
192 attr = (x==curr? ms->revinactattr : ms->inactattr);
195 lchar[0] = SUBMENUCHAR; lchar[1] = 0;
198 lchar[0] = RADIOMENUCHAR; lchar[1] = 0;
201 lchar[0] = (ci->itemdata.checked ? CHECKED : UNCHECKED);
205 fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0;
206 lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[2] = 0;
210 fchar[0] = EXITMENUCHAR; fchar[1] = 0;
212 default: // Just to keep the compiler happy
215 gotoxy(top+row,left-2,ms->menupage);
216 cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
217 gotoxy(top+row,left-2,ms->menupage);
218 csprint(fchar,attr[NOHLITE]); // Print first part
219 gotoxy(top+row,left,ms->menupage);
220 printmenuitem(str,attr); // Print main part
221 gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
222 csprint(lchar,attr[NOHLITE]); // Print last part
224 // Check if we need to MOREABOVE and MOREBELOW to be added
227 x = next_visible_sep(menu,0); // First item
228 if (! isvisible(menu,first,x)) // There is more above
231 gotoxy(top,left+menuwidth,ms->menupage);
232 cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage);
234 x = prev_visible_sep(menu,menu->numitems); // last item
235 if (! isvisible(menu,first,x)) // There is more above
238 gotoxy(top+numitems-1,left+menuwidth,ms->menupage);
239 cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage);
242 x = ((numitems-1)*curr)/(menu->numitems);
243 if ((x>0) && (row==1)) {
244 gotoxy(top+x,left+menuwidth,ms->menupage);
245 cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage);
247 if (ms->handler) ms->handler(ms,menu->items[curr]);
250 // Difference between this and regular menu, is that only
251 // OPT_INVISIBLE, OPT_SEP are honoured
252 void printradiomenu(pt_menu menu, int curr, uchar top, uchar left, int first)
254 int x,row; // x = index, row = position from top
255 int numitems,menuwidth;
256 char fchar[5],lchar[5]; // The first and last char in for each entry
257 const char *str; // and inbetween the item or a seperator is printed
258 uchar *attr; // all in the attribute attr
259 char sep[MENULEN];// and inbetween the item or a seperator is printed
262 numitems = calc_visible(menu,first);
263 if (numitems > menu->menuheight) numitems = menu->menuheight;
265 menuwidth = menu->menuwidth+3;
266 clearwindow(top,left-2, top+numitems+1, left+menuwidth+1,
267 ms->menupage, ms->fillchar, ms->shadowattr);
268 drawbox(top-1,left-3,top+numitems,left+menuwidth,
269 ms->menupage,ms->normalattr[NOHLITE],ms->menubt);
270 memset(sep,ms->box_horiz,menuwidth); // String containing the seperator string
271 sep[menuwidth-1] = 0;
273 x = (menuwidth - strlen(menu->title) - 1) >> 1;
274 gotoxy(top-1,left+x,ms->menupage);
275 printmenuitem(menu->title,ms->normalattr);
276 row = -1; // 1 less than inital value of x
277 for (x=first; x < menu->numitems; x++)
280 if (ci->action == OPT_INVISIBLE) continue;
282 if (row > numitems) break;
283 // Setup the defaults now
284 fchar[0] = RADIOUNSEL; fchar[1]='\0'; // Unselected ( )
285 lchar[0] = '\0'; // Nothing special after
286 str = ci->item; // Pointer to item string
287 attr = ms->normalattr; // Always same attribute
288 fchar[0] = (x==curr ? RADIOSEL : RADIOUNSEL);
289 switch (ci->action) // set up attr,str,fchar,lchar for everything
292 attr = ms->inactattr;
295 fchar[0] = '\b'; fchar[1] = ms->box_ltrt; fchar[2] = ms->box_horiz; fchar[3] = ms->box_horiz; fchar[4] = 0;
296 lchar[0] = ms->box_horiz; lchar[1] = ms->box_rtlt; lchar[3] = 0;
299 default: // To keep the compiler happy
302 gotoxy(top+row,left-2,ms->menupage);
303 cprint(ms->spacechar,attr[NOHLITE],menuwidth+2,ms->menupage); // Wipe area with spaces
304 gotoxy(top+row,left-2,ms->menupage);
305 csprint(fchar,attr[NOHLITE]); // Print first part
306 gotoxy(top+row,left,ms->menupage);
307 printmenuitem(str,attr); // Print main part
308 gotoxy(top+row,left+menuwidth-1,ms->menupage); // Last char if any
309 csprint(lchar,attr[NOHLITE]); // Print last part
311 // Check if we need to MOREABOVE and MOREBELOW to be added
314 x = next_visible_sep(menu,0); // First item
315 if (! isvisible(menu,first,x)) // There is more above
318 gotoxy(top,left+menuwidth,ms->menupage);
319 cprint(MOREABOVE,ms->normalattr[NOHLITE],1,ms->menupage);
321 x = prev_visible_sep(menu,menu->numitems); // last item
322 if (! isvisible(menu,first,x)) // There is more above
325 gotoxy(top+numitems-1,left+menuwidth,ms->menupage);
326 cprint(MOREBELOW,ms->normalattr[NOHLITE],1,ms->menupage);
329 x = ((numitems-1)*curr)/(menu->numitems);
330 if ((x > 0) && (row == 1))
332 gotoxy(top+x,left+menuwidth,ms->menupage);
333 cprint(SCROLLBOX,ms->normalattr[NOHLITE],1,ms->menupage);
335 if (ms->handler) ms->handler(ms,menu->items[curr]);
338 void cleanupmenu(pt_menu menu, uchar top,uchar left,int numitems)
340 if (numitems > menu->menuheight) numitems = menu->menuheight;
341 clearwindow(top,left-2, top+numitems+1, left+menu->menuwidth+4,
342 ms->menupage, ms->fillchar, ms->fillattr); // Clear the shadow
343 clearwindow(top-1, left-3, top+numitems, left+menu->menuwidth+3,
344 ms->menupage, ms->fillchar, ms->fillattr); // main window
347 /* Handle a radio menu */
348 pt_menuitem getradiooption(pt_menu menu, uchar top, uchar left, uchar startopt)
349 // Return item chosen or NULL if ESC was hit.
351 int curr,i,first,tmp;
354 pt_menuitem ci; // Current item
356 numitems = calc_visible(menu,0);
358 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
359 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
361 // Initialise current menu item
362 curr = next_visible(menu,startopt);
364 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
365 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
366 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
367 printmenuitem(menu->items[curr]->status,ms->statusattr);
368 first = calc_first_early(menu,curr);
371 printradiomenu(menu,curr,top,left,first);
372 ci = menu->items[curr];
378 curr = next_visible(menu,0);
379 first = calc_first_early(menu,curr);
382 curr = prev_visible(menu,numitems-1);
383 first = calc_first_late(menu,curr);
386 for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
387 first = calc_first_late(menu,curr);
390 for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
391 first = calc_first_early(menu,curr);
394 curr = prev_visible(menu,curr-1);
395 if (curr < first) first = calc_first_early(menu,curr);
398 curr = next_visible(menu,curr+1);
399 if (! isvisible(menu,first,curr))
400 first = calc_first_late(menu,curr);
409 if (ci->action == OPT_INACTIVE) break;
410 if (ci->action == OPT_SEP) break;
414 // Check if this is a shortcut key
415 if (((asc >= 'A') && (asc <= 'Z')) ||
416 ((asc >= 'a') && (asc <= 'z')) ||
417 ((asc >= '0') && (asc <= '9')))
419 tmp = find_shortcut(menu,asc,curr);
420 if ((tmp > curr) && (! isvisible(menu,first,tmp)))
421 first = calc_first_late(menu,tmp);
423 first = calc_first_early(menu,tmp);
427 if (ms->keys_handler) // Call extra keys handler
428 ms->keys_handler(ms,menu->items[curr],(scan << 8) | asc);
432 // Update status line
433 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
434 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
435 printmenuitem(menu->items[curr]->status,ms->statusattr);
437 return NULL; // Should never come here
440 /* Handle one menu */
441 pt_menuitem getmenuoption(pt_menu menu, uchar top, uchar left, uchar startopt)
442 // Return item chosen or NULL if ESC was hit.
444 int curr,i,first,tmp;
447 pt_menuitem ci; // Current item
448 t_handler_return hr; // Return value of handler
450 numitems = calc_visible(menu,0);
452 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
453 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
455 // Initialise current menu item
456 curr = next_visible(menu,startopt);
458 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
459 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,1);
460 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
461 printmenuitem(menu->items[curr]->status,ms->statusattr);
462 first = calc_first_early(menu,curr);
465 printmenu(menu,curr,top,left,first);
466 ci = menu->items[curr];
471 curr = next_visible(menu,0);
472 first = calc_first_early(menu,curr);
475 curr = prev_visible(menu,numitems-1);
476 first = calc_first_late(menu,curr);
479 for (i=0; i < 5; i++) curr = next_visible(menu,curr+1);
480 first = calc_first_late(menu,curr);
483 for (i=0; i < 5; i++) curr = prev_visible(menu,curr-1);
484 first = calc_first_early(menu,curr);
487 curr = prev_visible(menu,curr-1);
488 if (curr < first) first = calc_first_early(menu,curr);
491 curr = next_visible(menu,curr+1);
492 if (! isvisible(menu,first,curr))
493 first = calc_first_late(menu,curr);
502 if (ci->action == OPT_INACTIVE) break;
503 if (ci->action == OPT_CHECKBOX) break;
504 if (ci->action == OPT_SEP) break;
505 if (ci->action == OPT_EXITMENU) return NULL; // As if we hit Esc
506 // If we are going into a radio menu, dont call handler, return ci
507 if (ci->action == OPT_RADIOMENU) return ci;
508 if (ci->handler != NULL) // Do we have a handler
510 hr = ci->handler(ms,ci);
511 if (hr.refresh) // Do we need to refresh
513 // Cleanup menu using old number of items
514 cleanupmenu(menu,top,left,numitems);
515 // Recalculate the number of items
516 numitems = calc_visible(menu,0);
518 printmenu(menu,curr,top,left,first);
520 if (hr.valid) return ci;
525 if (ci->action != OPT_CHECKBOX) break;
526 ci->itemdata.checked = !ci->itemdata.checked;
527 if (ci->handler != NULL) // Do we have a handler
529 hr = ci->handler(ms,ci);
530 if (hr.refresh) // Do we need to refresh
532 // Cleanup menu using old number of items
533 cleanupmenu(menu,top,left,numitems);
534 // Recalculate the number of items
535 numitems = calc_visible(menu,0);
537 printmenu(menu,curr,top,left,first);
542 // Check if this is a shortcut key
543 if (((asc >= 'A') && (asc <= 'Z')) ||
544 ((asc >= 'a') && (asc <= 'z')) ||
545 ((asc >= '0') && (asc <= '9')))
547 tmp = find_shortcut(menu,asc,curr);
548 if ((tmp > curr) && (! isvisible(menu,first,tmp)))
549 first = calc_first_late(menu,tmp);
551 first = calc_first_early(menu,tmp);
555 if (ms->keys_handler) // Call extra keys handler
556 ms->keys_handler(ms,menu->items[curr],(scan << 8) | asc);
560 // Update status line
561 gotoxy(ms->minrow+ms->statline,ms->mincol,ms->menupage);
562 cprint(ms->spacechar,ms->statusattr[NOHLITE],ms->numcols,ms->menupage);
563 printmenuitem(menu->items[curr]->status,ms->statusattr);
565 return NULL; // Should never come here
568 /* Handle the entire system of menu's. */
569 pt_menuitem runmenusystem(uchar top, uchar left, pt_menu cmenu, uchar startopt, uchar menutype)
572 * Which menu should be currently displayed
574 * What is the position of the top,left corner of the menu
576 * which menu item do I start with
578 * NORMALMENU or RADIOMENU
581 * Returns a pointer to the final item chosen, or NULL if nothing chosen.
584 pt_menuitem opt,choice;
588 if (cmenu == NULL) return NULL;
590 // Set the menu height
591 cmenu->menuheight = ms->maxrow - top-3;
592 if (cmenu->menuheight > ms->maxmenuheight)
593 cmenu->menuheight = ms->maxmenuheight;
594 if (menutype == NORMALMENU)
595 opt = getmenuoption(cmenu,top,left,startopt);
596 else // menutype == RADIOMENU
597 opt = getradiooption(cmenu,top,left,startopt);
602 cleanupmenu(cmenu,top,left,calc_visible(cmenu,0));
605 // Are we done with the menu system?
606 if ((opt->action != OPT_SUBMENU) && (opt->action != OPT_RADIOMENU))
608 cleanupmenu(cmenu,top,left,calc_visible(cmenu,0));
609 return opt; // parent cleanup other menus
611 // Either radiomenu or submenu
612 // Do we have a valid menu number? The next hack uses the fact that
613 // itemdata.submenunum = itemdata.radiomenunum (since enum data type)
614 if (opt->itemdata.submenunum >= ms->nummenus) // This is Bad....
616 gotoxy(12,12,ms->menupage); // Middle of screen
617 csprint("ERROR: Invalid submenu requested.",0x07);
618 cleanupmenu(cmenu,top,left,calc_visible(cmenu,0));
619 return NULL; // Pretend user hit esc
621 // Call recursively for submenu
622 // Position the submenu below the current item,
623 // covering half the current window (horizontally)
624 row = ms->menus[(unsigned int)opt->itemdata.submenunum]->row;
625 col = ms->menus[(unsigned int)opt->itemdata.submenunum]->col;
626 if (row == 0xFF) row = top+opt->index+2;
627 if (col == 0xFF) col = left+3+(cmenu->menuwidth >> 1);
628 mt = (opt->action == OPT_SUBMENU ? NORMALMENU : RADIOMENU );
630 if ((opt->action == OPT_RADIOMENU) && (opt->data != NULL))
631 startat = ((t_menuitem *)opt->data)->index;
633 choice = runmenusystem(row, col,
634 ms->menus[(unsigned int)opt->itemdata.submenunum],
636 if (opt->action == OPT_RADIOMENU)
638 if (choice != NULL) opt->data = (void *)choice; // store choice in data field
639 if (opt->handler != NULL) opt->handler(ms,opt);
640 choice = NULL; // Pretend user hit esc
642 if (choice==NULL) // User hit Esc in submenu
645 startopt = opt->index;
650 cleanupmenu(cmenu,top,left,calc_visible(cmenu,0));
655 /* User Callable functions */
657 pt_menuitem showmenus(uchar startmenu)
662 // Setup screen for menusystem
663 oldpage = getdisppage();
664 setdisppage(ms->menupage);
666 clearwindow(ms->minrow, ms->mincol, ms->maxrow, ms->maxcol,
667 ms->menupage, ms->fillchar, ms->fillattr);
668 tpos = (ms->numcols - strlen(ms->title) - 1) >> 1; // center it on line
669 gotoxy(ms->minrow,ms->mincol,ms->menupage);
670 cprint(ms->tfillchar,ms->titleattr,ms->numcols,ms->menupage);
671 gotoxy(ms->minrow,ms->mincol+tpos,ms->menupage);
672 csprint(ms->title,ms->titleattr);
674 cursoroff(); // Doesn't seem to work?
676 // Go, main menu cannot be a radio menu
677 rv = runmenusystem(ms->minrow+MENUROW, ms->mincol+MENUCOL,
678 ms->menus[(unsigned int)startmenu], 0, NORMALMENU);
680 // Hide the garbage we left on the screen
682 if (oldpage == ms->menupage) cls(); else setdisppage(oldpage);
684 // Return user choice
688 pt_menusystem init_menusystem(const char *title)
693 ms = (pt_menusystem) malloc(sizeof(t_menusystem));
694 if (ms == NULL) return NULL;
696 // Initialise all menu pointers
697 for (i=0; i < MAXMENUS; i++) ms->menus[i] = NULL;
699 ms->title = (char *)malloc(TITLELEN+1);
701 strcpy(ms->title,TITLESTR); // Copy string
702 else strcpy(ms->title,title);
705 ms->tm_stepsize = TIMEOUTSTEPSIZE;
706 ms->tm_numsteps = TIMEOUTNUMSTEPS;
708 ms->normalattr[NOHLITE] = NORMALATTR;
709 ms->normalattr[HLITE] = NORMALHLITE;
711 ms->reverseattr[NOHLITE] = REVERSEATTR;
712 ms->reverseattr[HLITE] = REVERSEHLITE;
714 ms->inactattr[NOHLITE] = INACTATTR;
715 ms->inactattr[HLITE] = INACTHLITE;
717 ms->revinactattr[NOHLITE] = REVINACTATTR;
718 ms->revinactattr[HLITE] = REVINACTHLITE;
720 ms->statusattr[NOHLITE] = STATUSATTR;
721 ms->statusattr[HLITE] = STATUSHLITE;
723 ms->statline = STATLINE;
724 ms->tfillchar= TFILLCHAR;
725 ms->titleattr= TITLEATTR;
727 ms->fillchar = FILLCHAR;
728 ms->fillattr = FILLATTR;
729 ms->spacechar= SPACECHAR;
730 ms->shadowattr = SHADOWATTR;
732 ms->menupage = MENUPAGE; // Usually no need to change this at all
734 // Initialise all handlers
736 ms->keys_handler = NULL;
737 ms->ontimeout=NULL; // No timeout handler
739 // Setup ACTION_{,IN}VALID
740 ACTION_VALID.valid=1;
741 ACTION_VALID.refresh=0;
742 ACTION_INVALID.valid = 0;
743 ACTION_INVALID.refresh = 0;
745 // Figure out the size of the screen we are in now.
746 // By default we use the whole screen for our menu
747 ms->minrow = ms->mincol = 0;
748 ms->numcols = getnumcols();
749 ms->numrows = getnumrows();
750 ms->maxcol = ms->numcols - 1;
751 ms->maxrow = ms->numrows - 1;
753 // How many entries per menu can we display at a time
754 ms->maxmenuheight = ms->maxrow - ms->minrow - 3;
755 if (ms->maxmenuheight > MAXMENUHEIGHT)
756 ms->maxmenuheight= MAXMENUHEIGHT;
758 // Set up the look of the box
759 set_box_type(MENUBOXTYPE);
763 void set_normal_attr(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected)
765 if (normal != 0xFF) ms->normalattr[0] = normal;
766 if (selected != 0xFF) ms->reverseattr[0] = selected;
767 if (inactivenormal != 0xFF) ms->inactattr[0] = inactivenormal;
768 if (inactiveselected != 0xFF) ms->revinactattr[0] = inactiveselected;
771 void set_normal_hlite(uchar normal, uchar selected, uchar inactivenormal, uchar inactiveselected)
773 if (normal != 0xFF) ms->normalattr[1] = normal;
774 if (selected != 0xFF) ms->reverseattr[1] = selected;
775 if (inactivenormal != 0xFF) ms->inactattr[1] = inactivenormal;
776 if (inactiveselected != 0xFF) ms->revinactattr[1] = inactiveselected;
779 void set_status_info(uchar statusattr, uchar statushlite, uchar statline)
781 if (statusattr != 0xFF) ms->statusattr[NOHLITE] = statusattr;
782 if (statushlite!= 0xFF) ms->statusattr[HLITE] = statushlite;
783 // statline is relative to minrow
784 if (statline >= ms->numrows) 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) ms->tfillchar = tfillchar;
791 if (titleattr != 0xFF) ms->titleattr = titleattr;
794 void set_misc_info(uchar fillchar, uchar fillattr,uchar spacechar, uchar shadowattr)
796 if (fillchar != 0xFF) ms->fillchar = fillchar;
797 if (fillattr != 0xFF) ms->fillattr = fillattr;
798 if (spacechar != 0xFF) ms->spacechar = spacechar;
799 if (shadowattr!= 0xFF) ms->shadowattr= shadowattr;
802 void set_box_type(boxtype bt)
806 bxc = getboxchars(bt);
807 ms->box_horiz = bxc[BOX_HORIZ]; // The char used to draw top line
808 ms->box_ltrt = bxc[BOX_LTRT];
809 ms->box_rtlt = bxc[BOX_RTLT];
812 void set_menu_options(uchar maxmenuheight)
814 if (maxmenuheight != 0xFF) ms->maxmenuheight = maxmenuheight;
817 // Set the window which menusystem should use
818 void set_window_size(uchar top, uchar left, uchar bot, uchar right)
822 if ((top > bot) || (left > right)) return; // Sorry no change will happen here
825 if (bot >= nr) bot = nr-1;
826 if (right >= nc) right = nc-1;
831 ms->numcols = right - left + 1;
832 ms->numrows = bot - top + 1;
833 if (ms->statline >= ms->numrows) ms->statline = ms->numrows - 1; // Clip statline if need be
836 void reg_handler( t_handler htype, void * handler)
838 // If bad value set to default screen handler
841 ms->keys_handler = (t_keys_handler) handler;
844 ms->handler = (t_menusystem_handler) handler;
849 void unreg_handler(t_handler htype)
853 ms->keys_handler = NULL;
861 void reg_ontimeout(t_timeout_handler handler, unsigned int numsteps, unsigned int stepsize)
863 ms->ontimeout = handler;
864 if (numsteps != 0) ms->tm_numsteps = numsteps;
865 if (stepsize != 0) ms->tm_stepsize = stepsize;
868 void unreg_ontimeout()
870 ms->ontimeout = NULL;
873 int next_visible(pt_menu menu, int index)
876 if (index < 0) ans = 0 ;
877 else if (index >= menu->numitems) ans = menu->numitems-1;
879 while ((ans < menu->numitems-1) &&
880 ((menu->items[ans]->action == OPT_INVISIBLE) ||
881 (menu->items[ans]->action == OPT_SEP)))
886 int prev_visible(pt_menu menu, int index) // Return index of prev visible
889 if (index < 0) ans = 0;
890 else if (index >= menu->numitems) ans = menu->numitems-1;
893 ((menu->items[ans]->action == OPT_INVISIBLE) ||
894 (menu->items[ans]->action == OPT_SEP)))
899 int next_visible_sep(pt_menu menu, int index)
902 if (index < 0) ans = 0 ;
903 else if (index >= menu->numitems) ans = menu->numitems-1;
905 while ((ans < menu->numitems-1) &&
906 (menu->items[ans]->action == OPT_INVISIBLE))
911 int prev_visible_sep(pt_menu menu, int index) // Return index of prev visible
914 if (index < 0) ans = 0;
915 else if (index >= menu->numitems) ans = menu->numitems-1;
918 (menu->items[ans]->action == OPT_INVISIBLE))
923 int calc_visible(pt_menu menu,int first)
927 if (menu == NULL) return 0;
929 for (i=first; i < menu->numitems; i++)
930 if (menu->items[i]->action != OPT_INVISIBLE) ans++;
934 // is curr visible if first entry is first?
935 int isvisible(pt_menu menu,int first, int curr)
937 if (curr < first) return 0;
938 return (calc_visible(menu,first)-calc_visible(menu,curr) < menu->menuheight);
941 // Calculate the first entry to be displayed
942 // so that curr is visible and make curr as late as possible
943 int calc_first_late(pt_menu menu,int curr)
947 nv = calc_visible(menu,0);
948 if (nv <= menu->menuheight) return 0;
949 // Start with curr and go back menu->menuheight times
951 for (i=0; i < menu->menuheight; i++)
952 ans = prev_visible_sep(menu,ans-1);
956 // Calculate the first entry to be displayed
957 // so that curr is visible and make curr as early as possible
958 int calc_first_early(pt_menu menu,int curr)
962 nv = calc_visible(menu,0);
963 if (nv <= menu->menuheight) return 0;
964 // Start with curr and go back till >= menu->menuheight
966 nv = calc_visible(menu,curr); // Already nv of them are visible
968 for (i=0; i < menu->menuheight - nv; i++)
969 ans = prev_visible_sep(menu,ans-1);
973 // Create a new menu and return its position
974 uchar add_menu(const char *title, int maxmenusize)
980 if (num >= MAXMENUS) return -1;
982 m = (pt_menu) malloc(sizeof(t_menu));
983 if (m == NULL) return -1;
989 m->maxmenusize = MAXMENUSIZE;
990 else m->maxmenusize = maxmenusize;
991 m->items = (pt_menuitem *) malloc(sizeof(pt_menuitem)*(m->maxmenusize));
992 for (i=0; i < m->maxmenusize; i++) m->items[i] = NULL;
994 m->title = (char *)malloc(MENULEN+1);
997 if (strlen(title) > MENULEN - 2)
998 strcpy(m->title,TITLELONG);
999 else strcpy(m->title,title);
1001 else strcpy(m->title,EMPTYSTR);
1002 m ->menuwidth = strlen(m->title);
1004 return ms->nummenus - 1;
1007 void set_menu_pos(uchar row,uchar col) // Set the position of this menu.
1011 m = ms->menus[ms->nummenus-1];
1016 pt_menuitem add_sep() // Add a separator to current menu
1021 m = (ms->menus[ms->nummenus-1]);
1023 mi = (pt_menuitem) malloc(sizeof(t_menuitem));
1024 if (mi == NULL) return NULL;
1025 m->items[(unsigned int)m->numitems] = mi;
1026 mi->handler = NULL; // No handler
1027 mi->item = mi->status = mi->data = NULL;
1028 mi->action = OPT_SEP;
1029 mi->index = m->numitems++;
1030 mi->parindex = ms->nummenus-1;
1036 // Add item to the "current" menu
1037 pt_menuitem add_item(const char *item, const char *status, t_action action,
1038 const char *data, uchar itemdata)
1043 uchar inhlite=0; // Are we inside hlite area
1045 m = (ms->menus[ms->nummenus-1]);
1047 mi = (pt_menuitem) malloc(sizeof(t_menuitem));
1048 if (mi == NULL) return NULL;
1049 m->items[(unsigned int) m->numitems] = mi;
1050 mi->handler = NULL; // No handler
1052 // Allocate space to store stuff
1053 mi->item = (char *)malloc(MENULEN+1);
1054 mi->status = (char *)malloc(STATLEN+1);
1055 mi->data = (char *)malloc(ACTIONLEN+1);
1058 if (strlen(item) > MENULEN) {
1059 strcpy(mi->item,ITEMLONG);
1061 strcpy(mi->item,item);
1063 if (strlen(mi->item) > m->menuwidth) m->menuwidth = strlen(mi->item);
1064 } else strcpy(mi->item,EMPTYSTR);
1067 if (strlen(status) > STATLEN) {
1068 strcpy(mi->status,STATUSLONG);
1070 strcpy(mi->status,status);
1072 } else strcpy(mi->status,EMPTYSTR);
1077 mi->helpid = 0xFFFF;
1078 inhlite = 0; // We have not yet seen an ENABLEHLITE char
1079 // Find the first char in [A-Za-z0-9] after ENABLEHLITE and not arg to control char
1082 if (*str == ENABLEHLITE)
1086 if (*str == DISABLEHLITE)
1090 if ( (inhlite == 1) &&
1091 (((*str >= 'A') && (*str <= 'Z')) ||
1092 ((*str >= 'a') && (*str <= 'z')) ||
1093 ((*str >= '0') && (*str <= '9'))))
1100 if ((mi->shortcut >= 'A') && (mi->shortcut <= 'Z')) // Make lower case
1101 mi->shortcut = mi->shortcut -'A'+'a';
1104 if (strlen(data) > ACTIONLEN) {
1105 strcpy(mi->data,ACTIONLONG);
1107 strcpy(mi->data,data);
1109 } else strcpy(mi->data,EMPTYSTR);
1114 mi->itemdata.submenunum = itemdata;
1117 mi->itemdata.checked = itemdata;
1120 mi->itemdata.radiomenunum = itemdata;
1121 mi->data = NULL; // No selection made
1123 default: // to keep the compiler happy
1126 mi->index = m->numitems++;
1127 mi->parindex = ms->nummenus-1;
1131 // Set the shortcut key for the current item
1132 void set_item_options(uchar shortcut,int helpid)
1137 m = (ms->menus[ms->nummenus-1]);
1138 if (m->numitems <= 0) return;
1139 mi = m->items[(unsigned int) m->numitems-1];
1141 if (shortcut != 0xFF) mi->shortcut = shortcut;
1142 if (helpid != 0xFFFF) mi->helpid = helpid;
1145 // Free internal datasutructures
1146 void close_menusystem(void)