simple menu: first cut at working submenus
authorH. Peter Anvin <hpa@zytor.com>
Tue, 19 Feb 2008 23:33:36 +0000 (15:33 -0800)
committerH. Peter Anvin <hpa@zytor.com>
Wed, 20 Feb 2008 00:51:24 +0000 (16:51 -0800)
With this edit, the submenu system seems to have minimal
functionality.

com32/menu/menu.h
com32/menu/menumain.c
com32/menu/readconfig.c

index 7f452a1..62b2f46 100644 (file)
@@ -35,6 +35,7 @@
 struct menu;
 
 enum menu_action {
+  MA_NONE,                     /* Undefined value */
   MA_CMD,                      /* Execute a command */
   MA_DISABLED,                 /* Disabled menu entry */
   MA_SUBMENU,                  /* This is a submenu entry */
index f512e63..84fe2b0 100644 (file)
@@ -95,6 +95,13 @@ display_entry(const struct menu_entry *entry, const char *attrib,
              const char *hotattrib, int width)
 {
   const char *p = entry->displayname;
+  bool is_submenu = (entry->action == MA_SUBMENU || entry->action == MA_GOTO);
+
+  if (!p)
+    p = "";
+
+  if (is_submenu)
+    width -= 2;
 
   while ( width ) {
     if ( *p ) {
@@ -115,6 +122,9 @@ display_entry(const struct menu_entry *entry, const char *attrib,
       width--;
     }
   }
+
+  if (is_submenu)
+    fputs(" >", stdout);
 }
 
 static void
@@ -743,6 +753,8 @@ run_menu(void)
       /* Clear and redraw whole screen */
       /* Enable ASCII on G0 and DEC VT on G1; do it in this order
         to avoid confusing the Linux console */
+      if (clear >= 2)
+       prepare_screen_for_menu();
       clear_screen();
       clear = 0;
       prev_entry = prev_top = -1;
@@ -809,7 +821,30 @@ run_menu(void)
       } else {
        done = 1;
       }
-      cmdline = cm->menu_entries[entry].cmdline;
+      cmdline = NULL;
+      if (done) {
+       switch (cm->menu_entries[entry].action) {
+       case MA_CMD:
+         cmdline = cm->menu_entries[entry].cmdline;
+         break;
+       case MA_SUBMENU:
+       case MA_GOTO:
+         done = 0;
+         clear = 2;
+         cm = cm->menu_entries[entry].submenu;
+         entry = top = 0;
+         break;
+       case MA_QUIT:
+         /* Quit menu system */
+         done = 1;
+         clear = 1;
+         draw_row(entry-top+4+VSHIFT, -1, top, 0, 0);
+         break;
+       default:
+         done = 0;
+         break;
+       }
+      }
       break;
 
     case KEY_UP:
@@ -1024,7 +1059,6 @@ int menu_main(int argc, char *argv[])
   }
 
   parse_configs(argv+1);
-  cm = start_menu;
 
   /* Some postprocessing for all menus */
   for (m = menu_list; m; m = m->next) {
@@ -1043,6 +1077,7 @@ int menu_main(int argc, char *argv[])
     return 1;                  /* Error! */
   }
 
+  cm = start_menu;
   for(;;) {
     cmdline = run_menu();
 
index 88b450f..4c2ecbd 100644 (file)
@@ -113,7 +113,9 @@ looking_at(char *line, const char *kwd)
   return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */
 }
 
-static struct menu *new_menu(struct menu *parent, const char *label)
+static struct menu * new_menu(struct menu *parent,
+                             struct menu_entry *parent_entry,
+                             const char *label)
 {
   struct menu *m = calloc(1, sizeof(struct menu));
   int i;
@@ -123,11 +125,16 @@ static struct menu *new_menu(struct menu *parent, const char *label)
   if (parent) {
     /* Submenu */
     m->parent = parent;
-    m->parent_entry = &parent->menu_entries[parent->nentries-1];
+    m->parent_entry = parent_entry;
+    parent_entry->action = MA_SUBMENU;
+    parent_entry->submenu = m;
 
     for (i = 0; i < MSG_COUNT; i++)
       m->messages[i] = refstr_get(parent->messages[i]);
 
+    refstr_put(m->messages[MSG_TITLE]);
+    m->messages[MSG_TITLE] = refstr_get(empty_string);
+
     memcpy(m->mparm, parent->mparm, sizeof m->mparm);
 
     m->allowedit = parent->allowedit;
@@ -175,21 +182,27 @@ struct labeldata {
   unsigned int menuseparator;
   unsigned int menudisabled;
   unsigned int menuindent;
+  enum menu_action action;
 };
 
+/* Menu currently being parsed */
+static struct menu *current_menu;
+
 static void
-record(struct menu *m, struct labeldata *ld, const char *append)
+clear_label_data(struct labeldata *ld)
 {
-  int i;
-  struct menu_entry *me;
-  const struct syslinux_ipappend_strings *ipappend;
+  refstr_put(ld->label);
+  refstr_put(ld->kernel);
+  refstr_put(ld->append);
+  refstr_put(ld->menulabel);
+  refstr_put(ld->passwd);
 
-  if (!ld->label)
-    return;                    /* Nothing defined */
+  memset(ld, 0, sizeof *ld);
+}
 
-  /* Hidden entries are recorded on a special "hidden menu" */
-  if (ld->menuhide)
-    m = hide_menu;
+static struct menu_entry *new_entry(struct menu *m)
+{
+  struct menu_entry *me;
 
   if (m->nentries >= m->nentries_space) {
     if (!m->nentries_space)
@@ -200,19 +213,60 @@ record(struct menu *m, struct labeldata *ld, const char *append)
     m->menu_entries = realloc(m->menu_entries, m->nentries_space*
                              sizeof(struct menu_entry));
   }
-  me = &m->menu_entries[m->nentries];
+
+  me = &m->menu_entries[m->nentries++];
+  memset(me, 0, sizeof *me);
+
+  *all_entries_end = me;
+  all_entries_end = &me->next;
+
+  return me;
+}
+
+static void consider_for_hotkey(struct menu *m, struct menu_entry *me)
+{
+  unsigned char *p =
+    (unsigned char *)strchr(me->displayname, '^');
+
+  if (me->action != MA_DISABLED) {
+    if ( p && p[1] ) {
+      int hotkey = p[1] & ~0x20;
+      if ( !m->menu_hotkeys[hotkey] ) {
+       me->hotkey = hotkey;
+       m->menu_hotkeys[hotkey] = me;
+      }
+    }
+  }
+}
+
+static void
+record(struct menu *m, struct labeldata *ld, const char *append)
+{
+  int i;
+  struct menu_entry *me;
+  const struct syslinux_ipappend_strings *ipappend;
+
+  if (!ld->label)
+    return;                    /* Nothing defined */
+
+  /* Hidden entries are recorded on a special "hidden menu" */
+  if (ld->menuhide)
+    m = hide_menu;
 
   if ( ld->label ) {
     char ipoptions[4096], *ipp;
     const char *a;
     char *s;
 
-    me->displayname = ld->menulabel ? ld->menulabel : refstr_get(ld->label);
-    me->label       = ld->label;
-    me->passwd      = ld->passwd;
+    me = new_entry(m);
+
+    me->displayname = ld->menulabel
+      ? refstr_get(ld->menulabel) : refstr_get(ld->label);
+    me->label       = refstr_get(ld->label);
+    me->passwd      = refstr_get(ld->passwd);
     me->helptext    = ld->helptext;
-    me->hotkey = 0;
-    me->action = MA_CMD;
+    me->hotkey             = 0;
+    me->action     = ld->action ? ld->action : MA_CMD;
 
     if ( ld->menuindent ) {
       const char *dn;
@@ -222,74 +276,83 @@ record(struct menu *m, struct labeldata *ld, const char *append)
       me->displayname = dn;
     }
 
-    if ( ld->menulabel ) {
-      unsigned char *p = (unsigned char *)strchr(ld->menulabel, '^');
-      if ( p && p[1] ) {
-       int hotkey = p[1] & ~0x20;
-       if ( !m->menu_hotkeys[hotkey] ) {
-         me->hotkey = hotkey;
-       }
-      }
-    }
-
-    ipp = ipoptions;
-    *ipp = '\0';
-    ipappend = syslinux_ipappend_strings();
-    for (i = 0; i < ipappend->count; i++) {
-      if ( (ld->ipappend & (1U << i)) && ipappend->ptr[i] )
-       ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
-    }
-
-    a = ld->append;
-    if ( !a )
-      a = append;
-    if ( !a || (a[0] == '-' && !a[1]) )
-      a = "";
-    s = a[0] ? " " : "";
-    if (ld->type == KT_KERNEL) {
-      rsprintf(&me->cmdline, "%s%s%s%s",
-              ld->kernel, s, a, ipoptions);
-    } else {
-      rsprintf(&me->cmdline, ".%s %s%s%s%s",
-              kernel_types[ld->type], ld->kernel, s, a, ipoptions);
-    }
-    refstr_put(ld->kernel);
-    ld->kernel = NULL;
-    refstr_put(ld->append);
-    ld->append = NULL;
-
     if ( ld->menuseparator ) {
       refstr_put(me->displayname);
       me->displayname = refstr_get(empty_string);
     }
 
     if ( ld->menuseparator || ld->menudisabled ) {
+      me->action   = MA_DISABLED;
+      refstr_put(me->label);
       me->label    = NULL;
+      refstr_put(me->passwd);
       me->passwd   = NULL;
-      me->action   = MA_DISABLED;
-
-      refstr_put(me->cmdline);
-      me->cmdline = NULL;
     }
 
-    ld->label = NULL;
-    ld->passwd = NULL;
+    if (ld->menulabel)
+      consider_for_hotkey(m, me);
+
+    switch (me->action) {
+    case MA_CMD:
+      ipp = ipoptions;
+      *ipp = '\0';
+      
+      if (ld->ipappend) {
+       ipappend = syslinux_ipappend_strings();
+       for (i = 0; i < ipappend->count; i++) {
+         if ( (ld->ipappend & (1U << i)) && ipappend->ptr[i] )
+           ipp += sprintf(ipp, " %s", ipappend->ptr[i]);
+       }
+      }
+
+      a = ld->append;
+      if ( !a )
+       a = append;
+      if ( !a || (a[0] == '-' && !a[1]) )
+       a = "";
+      s = a[0] ? " " : "";
+      if (ld->type == KT_KERNEL) {
+       rsprintf(&me->cmdline, "%s%s%s%s",
+                ld->kernel, s, a, ipoptions);
+      } else {
+       rsprintf(&me->cmdline, ".%s %s%s%s%s",
+                kernel_types[ld->type], ld->kernel, s, a, ipoptions);
+      }
+      break;
 
-    if ( me->hotkey )
-      m->menu_hotkeys[me->hotkey] = me;
-    
-    if ( ld->menudefault && !ld->menudisabled && !ld->menuseparator )
-      m->defentry = m->nentries;
+    case MA_GOTO_UNRES:
+      me->cmdline = refstr_get(ld->kernel);
+      break;
 
-    *all_entries_end = me;
-    all_entries_end = &me->next;
+    default:
+      break;
+    }
 
-    m->nentries++;
+    if ( ld->menudefault && me->action == MA_CMD )
+      m->defentry = m->nentries-1;
   }
+
+  clear_label_data(ld);
 }
 
-static const char *
-unlabel(const char *str)
+static struct menu *begin_submenu(const char *tag)
+{
+  struct menu_entry *me;
+
+  if (!tag[0])
+    tag = NULL;
+
+  me = new_entry(current_menu);
+  me->displayname = refstrdup(tag);
+  return new_menu(current_menu, me, tag);
+}
+
+static struct menu *end_submenu(void)
+{
+  return current_menu->parent ? current_menu->parent : current_menu;
+}
+
+static const char *unlabel(const char *str)
 {
   /* Convert a CLI-style command line to an executable command line */
   const char *p;
@@ -477,8 +540,6 @@ static char *is_fkey(char *cmdstr, int *fkeyno)
   return q;
 }
 
-static struct menu *current_menu;
-
 static void parse_config_file(FILE *f)
 {
   char line[MAX_LINE], *p, *ep, ch;
@@ -507,14 +568,25 @@ static void parse_config_file(FILE *f)
        } else if ( m->parent_entry ) {
          refstr_put(m->parent_entry->displayname);
          m->parent_entry->displayname = refstrdup(skipspace(p+5));
+         consider_for_hotkey(m, m->parent_entry);
+         if (!m->messages[MSG_TITLE][0]) {
+           /* MENU LABEL -> MENU TITLE on submenu */
+           refstr_put(m->messages[MSG_TITLE]);
+           m->messages[MSG_TITLE] = refstr_get(m->parent_entry->displayname);
+         }
        }
       } else if ( looking_at(p, "default") ) {
        ld.menudefault = 1;
       } else if ( looking_at(p, "hide") ) {
        ld.menuhide = 1;
       } else if ( looking_at(p, "passwd") ) {
-       refstr_put(ld.passwd);
-       ld.passwd = refstrdup(skipspace(p+6));
+       if ( ld.label ) {
+         refstr_put(ld.passwd);
+         ld.passwd = refstrdup(skipspace(p+6));
+       } else if ( m->parent_entry ) {
+         refstr_put(m->parent_entry->passwd);
+         m->parent_entry->passwd = refstrdup(skipspace(p+6));
+       }
       } else if ( looking_at(p, "shiftkey") ) {
        shiftkey = 1;
       } else if ( looking_at(p, "onerror") ) {
@@ -625,17 +697,32 @@ static void parse_config_file(FILE *f)
        }
        set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow);
       } else if ( looking_at(p, "separator") ) {
-        record(root_menu, &ld, append);
-        memset(&ld, 0, sizeof(struct labeldata));
-        ld.label = "";
+        record(m, &ld, append);
+        ld.label = refstr_get(empty_string);
        ld.menuseparator = 1;
-        record(root_menu, &ld, append);
-        memset(&ld, 0, sizeof(struct labeldata));
+        record(m, &ld, append);
       } else if ( looking_at(p, "disable") ||
                  looking_at(p, "disabled")) {
        ld.menudisabled = 1;
       } else if ( looking_at(p, "indent") ) {
         ld.menuindent = atoi(skipspace(p+6));
+      } else if ( looking_at(p, "begin") ) {
+        record(m, &ld, append);
+       m = current_menu = begin_submenu(skipspace(p+5));
+      } else if ( looking_at(p, "end") ) {
+        record(m, &ld, append);
+       m = current_menu = end_submenu();
+      } else if ( looking_at(p, "quit") ) {
+       if (ld.label)
+         ld.action = MA_QUIT;
+      } else if ( looking_at(p, "goto") ) {
+       if (ld.label) {
+         ld.action = MA_GOTO_UNRES;
+         refstr_put(ld.kernel);
+         ld.kernel = refstrdup(skipspace(p+4));
+       }
+      } else if ( looking_at(p, "start") ) {
+       start_menu = m;
       } else {
        /* Unknown, check for layout parameters */
        enum parameter_number mp;
@@ -707,7 +794,7 @@ static void parse_config_file(FILE *f)
       }
     } else if ( looking_at(p, "label") ) {
       p = skipspace(p+5);
-      record(root_menu, &ld, append);
+      record(m, &ld, append);
       ld.label     = refstrdup(p);
       ld.kernel    = refstrdup(p);
       ld.type      = KT_KERNEL;
@@ -786,8 +873,8 @@ void parse_configs(char **argv)
   empty_string = refstrdup("");
 
   /* Initialize defaults for the root and hidden menus */
-  hide_menu = new_menu(NULL, ".hidden");
-  root_menu = new_menu(NULL, ".top");
+  hide_menu = new_menu(NULL, NULL, ".hidden");
+  root_menu = new_menu(NULL, NULL, ".top");
   start_menu = root_menu;
 
   /* Other initialization */