// anilinmen.c --- A simple Ncurses menu for Linux with menuname,variable args support.
/*+---------------------------------------------+*/
/*|Copyright (C) 2003 Anirudh Sasikumar         |*/
/*|                                             |*/
/*|Author: Anirudh Sasikumar <an1sk@hotmail.com>|*/
/*|Created: January 3 2003                      |*/
/*+---------------------------------------------+*/
/* 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; either version 2 of the License, or */
/* (at your option) any later version. */

/* This program is distributed in the hope that it will be useful, */
/* but WITHOUT ANY WARRANTY; without even the implied warranty of */
/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the */
/* GNU General Public License for more details. */

/* You should have received a copy of the GNU General Public License */
/* along with this program; if not, write to the Free Software */
/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */

/* Commentary: */
/* This was written during my first experimentations with ncurses and */
/* linux programming and completed within half an hour in the lab. */
/* Haven't polished it. Read and realize the evil ways of poor*/
/* commenting!*/
/* Make sure you pass -lncurses to gcc while compiling.*/

#include <stdio.h>
#include <stdarg.h>
#include <curses.h>
#include <string.h>

#define MAXMENU 25 //Maximum number of entries in the menu
#define ENTERKEY 10
#define UPKEY 66
#define DOWNKEY 65

//Convert string to uppercase and return allocated string
char *touppers(const char *s)
{
  int i=0,size=strlen(s)+1;
  char *buf;
  buf=(char *)calloc(size,sizeof(char));
  strncpy(buf,s,size);
  while(i<size)
    {
      if(buf[i]>='a'&&buf[i]<='z')
	buf[i]=buf[i]+'A'-'a';
      i++;
    }
  buf[i]='\0';
  return buf;
}

int displaymenu(char *name,char *list1,char *list2,...)
{
  int choice=0,currkey=0,currpos=0;
  int i=0,size=1,strcnt=0;
  int xpos=1,ypos=1,maxx=1,maxy=20;
  char *stringlist[MAXMENU];
  char *temp=0,*temp1;
  va_list menulist;
  noecho();
  refresh();
  getmaxyx(stdscr,maxy,maxx);
  temp=touppers(list2);
  stringlist[0]=strdup(list1);
  if(strcmp(temp,"EXIT")!=0) //create stringlist if there are more than 2 menu items
    {
      va_start(menulist,list2);
      temp1=touppers(temp);
      temp=list2;
      //loop to copy variable args to a SAFE stringlist
      //Have had bad experiences in DOS. Though in Linux I guess this is unnecessary.
      //There ought to be a more elegant way to this!!
      for(;strcmp(temp1,"EXIT")!=0;temp=va_arg(menulist,char *),temp1=touppers(temp))
	{
	  free(temp1);
	  if(size>=MAXMENU) break;
	  strcnt=strlen(temp);
	  if(strcnt)
	    {
	      stringlist[size]=(char *)calloc(strcnt+1,sizeof(char));
	      for(i=0;i<strcnt;i++)
		stringlist[size][i]=temp[i];
	      stringlist[size][i]='\0';
	    }
	  else
	    break;
	  size++;
	}

      va_end(menulist);
    }
  else
    {
      //do stuff to display menu when there are only 2 options
      stringlist[1]=strdup(list2);
      size=2;
    }
  stringlist[size++]=strdup("Exit");  //to copy exit to stringlist
  //to display the menu
  getyx(stdscr,ypos,xpos);
  if ((ypos+size+2)>=maxy)
    clear();
  do
  {
    move(ypos,xpos);
    refresh();
    attron(A_UNDERLINE);
    printw("%s\n",name);
    attroff(A_UNDERLINE);
    for(i=0;i<size;i++)
      {
	if(currpos==i)
	  attron(A_REVERSE);
	printw("%d.%s\n",i+1,stringlist[i]);
	if(currpos==i)
	  attroff(A_REVERSE);
      }
    attron(A_BOLD);
    printw("Press (UP/DOWN arrow key to navigate) (Enter or [1-%d] to choose) ",size);
    attroff(A_BOLD);
    if (choice)
      currkey=ENTERKEY;
    else
      currkey=getch();
    if (currkey>='1'&&currkey<=('0'+size))
      {
	choice=currkey;
	currpos=currkey-'1';
	//to clear any error messages printed
	printw("\n\n");
      }
    else if (currkey==ENTERKEY)
      {
	printw("\n\n");
	break;
      }
    else if (currkey==DOWNKEY)
      {
	printw("\n\n");
	if (currpos==0)
	  currpos=size-1;
	else
	  currpos--;
      }
    else if (currkey==UPKEY)
      {
	printw("\n\n");
	if (currpos==size-1)
	  currpos=0;
	else
	  currpos++;
      }
    else
      {
	printw("\nInvalid option... Please press keys (1-%d)\n",size);
      }
  }
    while(currkey!=10);
  //to clean up stringlist
 for(i=0;i<size;i++)
   {
	  free(stringlist[i]);
	}

 // getch(); //decomment if you want to have a pause after reading option
 return currpos+1;
}


int main(int argc,char **argv)
{
  int choice=0;
  initscr();
  cbreak();
  refresh();
  printw("Display Menu Demo.\n");
  //Be careful to pass Exit to displaymenu, otherwise prepare for an infinite loop.
  choice=displaymenu("Main Menu (displaymenu demo)","Main Option 1",
		     "Launch SubMenu","Main Option 3","Exit");
  //Now variable i has the user's choice
  printw("You chose option %d.\n",choice);
  if (choice==2)
    {
      choice=displaymenu("Child Menu (displaymenu demo)","Child Option 1",
			 "Child Option 2","Exit");
      printw("You chose option %d.\n",choice);
    }
  printw("Press Any Key to Exit");
  getch();
  endwin();
  return 0;
}


Copyright © 2004-2011 Anirudh Sasikumar. All rights reserved.
Last Updated: November 29, 2011 4:41 PM