aboutsummaryrefslogtreecommitdiffstats
path: root/menu/menu.c
diff options
context:
space:
mode:
Diffstat (limited to 'menu/menu.c')
-rw-r--r--menu/menu.c407
1 files changed, 407 insertions, 0 deletions
diff --git a/menu/menu.c b/menu/menu.c
new file mode 100644
index 00000000..74acad0e
--- /dev/null
+++ b/menu/menu.c
@@ -0,0 +1,407 @@
+/* -*- c -*- ------------------------------------------------------------- *
+ *
+ * Copyright 2004 Murali Krishnan Ganapathy - All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, Inc., 53 Temple Place Ste 330,
+ * Bostom MA 02111-1307, USA; either version 2 of the License, or
+ * (at your option) any later version; incorporated herein by reference.
+ *
+ * ----------------------------------------------------------------------- */
+
+#include "biosio.h"
+#include "string.h"
+#include "menu.h"
+
+// Structures
+
+// Declare a menusystem here
+static t_menusystem menusystem;
+
+/* Basic Menu routines */
+
+void drawbox(char top, char left, char bot, char right,char attr, char page)
+{
+ char x;
+
+ // Top border
+ gotoxy(top,left,page);
+ cprint(TOPLEFT,attr,1,page);
+ gotoxy(top,left+1,page);
+ cprint(TOP,attr,right-left,page);
+ gotoxy(top,right,page);
+ cprint(TOPRIGHT,attr,1,page);
+ // Bottom border
+ gotoxy(bot,left,page);
+ cprint(BOTLEFT,attr,1,page);
+ gotoxy(bot,left+1,page);
+ cprint(BOT,attr,right-left,page);
+ gotoxy(bot,right,page);
+ cprint(BOTRIGHT,attr,1,page);
+ // Left & right borders
+ for (x=top+1; x < bot; x++)
+ {
+ gotoxy(x,left,page);
+ cprint(LEFT,attr,1,page);
+ gotoxy(x,right,page);
+ cprint(RIGHT,attr,1,page);
+ }
+}
+
+void printmenu(t_menu * menu, int curr, char top, char left)
+{
+ int x;
+ int numitems,menuwidth;
+ t_menusystem *ms;
+ char attr;
+
+ ms = & menusystem;
+ numitems = menu->numitems;
+ menuwidth = menu->menuwidth+2;
+ clearwindow(top,left-1,top+numitems+1,left+menuwidth+1,ms->menupage,ms->fillchar,ms->shadowattr);
+ drawbox(top-1,left-2,top+numitems,left+menuwidth,ms->normalattr,ms->menupage);
+ // Menu title
+ x = (menuwidth - strlen(menu->title) - 1) >> 1;
+ gotoxy(top-1,left+x,ms->menupage);
+ sprint(menu->title);
+ for (x=0; x < numitems; x++)
+ {
+ gotoxy(top+x,left-1,ms->menupage);
+ if (menu->items[x].action == OPT_INACTIVE)
+ {
+ attr = (x==curr? ms->revinactattr : ms->inactattr);
+ } else {
+ attr = (x==curr ? ms->reverseattr : ms->normalattr);
+ }
+ cprint(ms->spacechar,attr,menuwidth+1,ms->menupage);
+ gotoxy(top+x,left,ms->menupage);
+ sprint(menu->items[x].item);
+ gotoxy(top+x,left+menuwidth-1,ms->menupage); // Last char if any
+ switch (menu->items[x].action)
+ {
+ case OPT_SUBMENU:
+ cprint(SUBMENUCHAR,attr,1,ms->menupage);
+ break;
+ case OPT_CHECKBOX:
+ cprint( (menu->items[x].itemdata.checked ? CHECKED : UNCHECKED),attr,1,ms->menupage);
+ break;
+ }
+ }
+ if (menusystem.handler) menusystem.handler(&menusystem,menu->items+curr);
+}
+
+void cleanupmenu(t_menu *menu, char top,char left)
+{
+ t_menusystem *ms = &menusystem;
+ clearwindow(top,left-1,top+menu->numitems+1,left+menu->menuwidth+3,ms->menupage,ms->fillchar,ms->fillattr); // Clear the shadow
+ clearwindow(top-1,left-2,top+menu->numitems,left+menu->menuwidth+2,ms->menupage,ms->fillchar,ms->fillattr); // clear the main window
+}
+
+/* Handle one menu */
+t_menuitem * getmenuoption( t_menu *menu, char top, char left, char startopt)
+// Return item chosen or NULL if ESC was hit.
+{
+ int curr;
+ char asc,scan;
+ char numitems;
+ t_menusystem *ms;
+ t_menuitem *ci; // Current item
+
+ ms = & menusystem;
+ numitems = menu->numitems;
+ // Setup status line
+ gotoxy(ms->statline,0,ms->menupage);
+ cprint(ms->spacechar,ms->reverseattr,80,ms->menupage);
+
+ // Initialise current menu item
+ curr = startopt;
+ gotoxy(ms->statline,0,ms->menupage);
+ cprint(ms->spacechar,ms->statusattr,80,1);
+ gotoxy(ms->statline,0,ms->menupage);
+ sprint(menu->items[curr].status);
+ while (1) // Forever
+ {
+ printmenu(menu,curr,top,left);
+ ci = &(menu->items[curr]);
+ asc = inputc(&scan);
+ switch (scan)
+ {
+ case HOMEKEY:
+ curr = 0;
+ break;
+ case ENDKEY:
+ curr = numitems -1;
+ break;
+ case PAGEDN:
+ curr += 5;
+ break;
+ case PAGEUP:
+ curr -= 5;
+ break;
+ case UPARROW:
+ curr --;
+ break;
+ case DNARROW:
+ curr++;
+ break;
+ case LTARROW:
+ case ESCAPE:
+ return NULL;
+ break;
+ case ENTERA:
+ case RTARROW:
+ case ENTERB:
+ if (ci->action == OPT_INACTIVE) break;
+ if (ci->action == OPT_CHECKBOX) break;
+ if (ci->action == OPT_EXITMENU) return NULL; // As if we hit Esc
+ return ci;
+ break;
+ case SPACEKEY:
+ if (ci->action != OPT_CHECKBOX) break;
+ ci->itemdata.checked = !ci->itemdata.checked;
+ // Call handler to see it anything needs to be done
+ if (ci->handler != NULL) ci->handler(&menusystem,ci);
+ break;
+ }
+ // Adjust within range
+ if (curr < 0) curr=0;
+ if (curr >= numitems) curr = numitems -1;
+ // Update status line
+ gotoxy(ms->statline,0,ms->menupage);
+ cprint(ms->spacechar,ms->statusattr,80,ms->menupage);
+ sprint(menu->items[curr].status);
+ }
+ return NULL; // Should never come here
+}
+
+/* Handle the entire system of menu's. */
+t_menuitem * runmenusystem(char top, char left, int currmenu)
+/*
+ * currmenu
+ * Which menu should be currently displayed
+ * top,left
+ * What is the position of the top,left corner of the menu
+ *
+ * Return Value:
+ * Returns a pointer to the final item chosen, or NULL if nothing chosen.
+ */
+{
+ t_menu *cmenu;
+ t_menusystem *ms = &menusystem;
+ t_menuitem *opt,*choice;
+ int numitems;
+ char startopt;
+
+ startopt = 0;
+startover:
+ cmenu = (menusystem.menus+currmenu);
+ numitems = menusystem.menus[currmenu].numitems;
+ opt = getmenuoption(cmenu,top,left,startopt);
+ if (opt == NULL)
+ {
+ // User hit Esc
+ cleanupmenu(cmenu,top,left);
+ return NULL;
+ }
+ if (opt->action != OPT_SUBMENU) // We are done with the menu system
+ {
+ cleanupmenu(cmenu,top,left);
+ return opt; // parent cleanup other menus
+ }
+ if (opt->itemdata.submenunum >= menusystem.nummenus) // This is Bad....
+ {
+ gotoxy(12,12,ms->menupage); // Middle of screen
+ sprint("Invalid submenu requested. Ask administrator to correct this.$");
+ cleanupmenu(cmenu,top,left);
+ return NULL; // Pretend user hit esc
+ }
+ // Call recursively for submenu
+ // Position the submenu below the current item,
+ // covering half the current window (horizontally)
+ choice = runmenusystem(top+opt->index+2, left+3+(cmenu->menuwidth >> 1), opt->itemdata.submenunum);
+ if (choice==NULL) // User hit Esc in submenu
+ {
+ // Startover
+ startopt = opt->index;
+ goto startover;
+ }
+ else
+ {
+ cleanupmenu(cmenu,top,left);
+ return choice;
+ }
+}
+
+/* User Callable functions */
+
+t_menuitem * showmenus(char startmenu)
+{
+ t_menuitem *rv;
+ t_menusystem *ms;
+ char oldpage,tpos;
+
+ ms = & menusystem;
+ // Setup screen for menusystem
+ oldpage = getdisppage();
+ setdisppage(ms->menupage);
+ clearwindow(0,0,24,79,ms->menupage,ms->fillchar,ms->fillattr);
+ tpos = (80 - strlen(menusystem.title) - 1) >> 1; // To center it on line
+ gotoxy(0,0,ms->menupage);
+ cprint(ms->tfillchar,ms->titleattr,80,ms->menupage);
+ gotoxy(0,tpos,ms->menupage);
+ sprint(menusystem.title);
+
+ cursoroff(); // Doesn't seem to work?
+
+ // Go
+ rv = runmenusystem(MENUROW, MENUCOL, startmenu);
+
+ // Hide the garbage we left on the screen
+ cursoron();
+ if (oldpage == ms->menupage) cls(); else setdisppage(oldpage);
+
+ // Return user choice
+ return rv;
+}
+
+void init_menusystem(const char *title)
+{
+ menusystem.nummenus = 0;
+ if (title == NULL)
+ dstrcpy(menusystem.title,TITLESTR);
+ else dstrcpy(menusystem.title,title);
+
+ menusystem.normalattr = NORMALATTR;
+ menusystem.reverseattr= REVERSEATTR;
+ menusystem.inactattr = INACTATTR;
+ menusystem.revinactattr = REVINACTATTR;
+
+ menusystem.statusattr = STATUSATTR;
+ menusystem.statline = STATLINE;
+ menusystem.tfillchar= TFILLCHAR;
+ menusystem.titleattr= TITLEATTR;
+
+ menusystem.fillchar = FILLCHAR;
+ menusystem.fillattr = FILLATTR;
+ menusystem.spacechar= SPACECHAR;
+ menusystem.shadowattr = SHADOWATTR;
+
+ menusystem.menupage = MENUPAGE; // Usually no need to change this at all
+ menusystem.handler = NULL; // No handler function
+}
+
+void set_normal_attr(char normal, char selected, char inactivenormal, char inactiveselected)
+{
+ if (normal != 0xFF) menusystem.normalattr = normal;
+ if (selected != 0xFF) menusystem.reverseattr = selected;
+ if (inactivenormal != 0xFF) menusystem.inactattr = inactivenormal;
+ if (inactiveselected != 0xFF) menusystem.revinactattr = inactiveselected;
+}
+
+void set_status_info(char statusattr, char statline)
+{
+ if (statusattr != 0xFF) menusystem.statusattr = statusattr;
+ if (statline != 0xFF) menusystem.statline = statline;
+}
+
+void set_title_info(char tfillchar, char titleattr)
+{
+ if (tfillchar != 0xFF) menusystem.tfillchar = tfillchar;
+ if (titleattr != 0xFF) menusystem.titleattr = titleattr;
+}
+
+void set_misc_info(char fillchar, char fillattr,char spacechar, char shadowattr)
+{
+ if (fillchar != 0xFF) menusystem.fillchar = fillchar;
+ if (fillattr != 0xFF) menusystem.fillattr = fillattr;
+ if (spacechar != 0xFF) menusystem.spacechar = spacechar;
+ if (shadowattr!= 0xFF) menusystem.shadowattr= shadowattr;
+}
+
+void reg_handler( t_menusystem_handler handler)
+{
+ menusystem.handler = handler;
+}
+
+void unreg_handler()
+{
+ menusystem.handler = NULL;
+}
+
+int add_menu(const char *title) // Create a new menu and return its position
+{
+ const int num = (unsigned char)menusystem.nummenus;
+
+ if (num >= MAXMENUS) return -1;
+ menusystem.menus[num].numitems = 0;
+ menusystem.menus[num].menuwidth = 0;
+ if (title)
+ {
+ if (strlen(title) > MENULEN - 2) {
+ dstrcpy(menusystem.menus[num].title," TITLE TOO LONG ");
+ } else {
+ dstrcpy(menusystem.menus[num].title,title);
+ }
+ }
+ else
+ {
+ dstrcpy(menusystem.menus[num].title,"");
+ }
+
+ return menusystem.nummenus++;
+}
+
+t_menuitem * add_item(const char *item, const char *status, t_action action, const char *data, char itemdata) // Add item to the "current" menu
+{
+ t_menuitem *mi;
+ t_menu *m;
+
+ m = &(menusystem.menus[menusystem.nummenus-1]);
+ mi = &(m->items[m->numitems]);
+ mi->handler = NULL; // No handler
+ if (item) {
+ if (strlen(item) > MENULEN - 2) {
+ dstrcpy(mi->item,"ITEM TOO LONG");
+ } else {
+ dstrcpy(mi->item,item);
+ if (strlen(item) > m->menuwidth) m->menuwidth = strlen(item);
+ }
+ } else dstrcpy(mi->item,"");
+
+ if (status) {
+ if (strlen(status) > STATLEN - 2) {
+ dstrcpy(mi->status,"STATUS STRING TOO LONG");
+ } else {
+ dstrcpy(mi->status,status);
+ }
+ } else dstrcpy(mi->status,"");
+
+ mi->action = action;
+
+ if (data) {
+ if (strlen(data) > ACTIONLEN - 2) {
+ strcpy(mi->data,"ACTION STRING LONG");
+ } else {
+ strcpy(mi->data,data); // This is only null terminated
+ }
+ } else strcpy(mi->data,"");
+
+ switch (action)
+ {
+ case OPT_SUBMENU:
+ mi->itemdata.submenunum = itemdata;
+ break;
+ case OPT_CHECKBOX:
+ mi->itemdata.checked = itemdata;
+ break;
+ case OPT_RADIOBTN:
+ mi->itemdata.choice = itemdata;
+ break;
+ }
+ mi->index = m->numitems++;
+ mi->parindex = menusystem.nummenus-1;
+ return mi;
+}
+
+