[BACK]Return to menu.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / othersrc / dist / cdk

File: [cvs.NetBSD.org] / othersrc / dist / cdk / menu.c (download)

Revision 1.3, Tue Jan 9 18:41:53 2001 UTC (13 years, 6 months ago) by garbled
Branch: MAIN
CVS Tags: HEAD
Changes since 1.2: +2 -3 lines

Fix a problem in these widgets where they uncondititonally set the
exitType to either vEARLY_EXIT, or vESCAPE_HIT when returning from a bound
function.  This had the unfortunate effect that when you hit F2 to refresh
the screen in sushi, it would exit out immediately after refreshing the
screen.

This modification allows the programmer to still create an exit-causing
bound function, by simply setting the exitType in the function, as was likely
intended by the author.  Many thanks to Charles Hannum for helping me figure
this out.

This should fix problems noted by itojun on tech-userlevel with the function
keys.

#include <cdk.h>

/*
 * $Author: garbled $
 * $Date: 2001/01/09 18:41:53 $
 * $Revision: 1.3 $
 */

/*
 * Declare file local prototypes.
 */
static void cleanUpMenu (CDKMENU *menu);

DeclareCDKObjects(my_funcs,Menu);

/*
 * This creates a new menu widget.
 */
CDKMENU *newCDKMenu (CDKSCREEN *cdkscreen, char *menulist[MAX_MENU_ITEMS][MAX_SUB_ITEMS], int menuItems, int *subsize, int *menuloc, int menuPos, chtype titleAttr, chtype subtitleAttr)
{
   CDKMENU *menu	= newCDKObject(CDKMENU, &my_funcs);
   int rightcount	= menuItems-1;
   int rightloc		= getmaxx((cdkscreen->window)) - 1;
   int leftloc		= 0;
   int x, y, max, junk;

   /* Start making a copy of the information. */
   ScreenOf(menu)		= cdkscreen;
   ObjOf(menu)->box		= FALSE;
   rightcount			= menuItems-1;
   menu->parent			= cdkscreen->window;
   menu->menuItems		= menuItems;
   menu->titleAttr		= titleAttr;
   menu->subtitleAttr		= subtitleAttr;
   menu->currentTitle		= 0;
   menu->currentSubtitle	= 0;
   menu->lastSelection		= -1;
   menu->preProcessFunction	= 0;
   menu->preProcessData		= 0;
   menu->postProcessFunction	= 0;
   menu->postProcessData	= 0;
   menu->menuPos		= menuPos;
   menu->exitType		= vNEVER_ACTIVATED;

   /* Create the pull down menus. */
   for (x=0; x < menuItems; x++)
   {
      max = -1;
      for (y=1; y < subsize[x] ; y++)
      {
	 if (menuloc[x] == LEFT)
	 {
	    menu->sublist[x][y-1] = char2Chtype (menulist[x][y], &menu->sublistLen[x][y-1], &junk);
	    max = MAXIMUM (max, menu->sublistLen[x][y-1]);
	 }
	 else
	 {
	    menu->sublist[rightcount][y-1] = char2Chtype (menulist[x][y], &menu->sublistLen[rightcount][y-1], &junk);
	    max = MAXIMUM (max, menu->sublistLen[rightcount][y-1]);
	 }
      }

      if (menuloc[x] == LEFT)
      {
	/*
	 * Its a menu item on the left, add it to the left side
	 * of the menu.
	 */
	 menu->title[x]		= char2Chtype (menulist[x][0], &menu->titleLen[x], &junk);
	 menu->subsize[x]	= subsize[x] - 1;
	 if (menu->menuPos == BOTTOM)
	 {
	    menu->titleWin[x]	= subwin (cdkscreen->window, 1, menu->titleLen[x]+2, LINES-1, leftloc);
	    menu->pullWin[x]	= subwin (cdkscreen->window, subsize[x]+1, max+2, LINES-subsize[x]-2, leftloc);
	 }
	 else
	 {
	    menu->titleWin[x]	= subwin (cdkscreen->window, 1, menu->titleLen[x]+2, 0, leftloc);
	    menu->pullWin[x]	= subwin (cdkscreen->window, subsize[x]+1, max+2, 1, leftloc);
	 }
	 leftloc += menu->titleLen[x] + 1;

	 /* Allow the windows to use the keypad. */
	 keypad (menu->titleWin[x], TRUE);
	 keypad (menu->pullWin[x], TRUE);
      }
      else 
      {
	/*
	 * Its a menu item on the right, add it to the right side
	 * of the menu.
	 */
	 rightloc			-= max + 3;
	 menu->title[rightcount]	= char2Chtype (menulist[x][0], &menu->titleLen[rightcount], &junk);
	 menu->subsize[rightcount]	= subsize[x] - 1;
	 if (menu->menuPos == BOTTOM)
	 {
	    menu->titleWin[rightcount]	= subwin (cdkscreen->window, 1, menu->titleLen[x]+2, LINES-1, rightloc);
	    menu->pullWin[rightcount]	= subwin (cdkscreen->window, subsize[x]+1, max+2, LINES-subsize[x]-2, rightloc);
	 }
	 else
	 {
	    menu->titleWin[rightcount]	= subwin (cdkscreen->window, 1, menu->titleLen[x]+2, 0, rightloc);
	    menu->pullWin[rightcount]	= subwin (cdkscreen->window, subsize[x]+1, max+2, 1, rightloc);
	 }

	 /* Allow the windows to use the keypad. */
	 keypad (menu->titleWin[rightcount], TRUE);
	 keypad (menu->pullWin[rightcount], TRUE);
	 rightcount--;
      }
   }

   /* Clean the key bindings. */
   cleanCDKObjectBindings (vMENU, menu);

   /* Register this baby. */
   registerCDKObject (cdkscreen, vMENU, menu);

   /* Return the menu object. */
   return (menu);
}

/*
 * This activates the CDK Menu.
 */
int activateCDKMenu (CDKMENU *menu, chtype *actions)
{
   chtype input;
   int ret;

   /* Draw in the screen. */
   refreshCDKScreen (ScreenOf(menu));

   /* Display the menu titles. */
   drawCDKMenu(menu, ObjOf(menu)->box);

   /* Highlight the current title and window. */
   drawCDKMenuSubwin (menu);

   /* If the input string is null, this is an interactive activate. */
   if (actions == 0)
   {
      /* Start taking input from the keyboard. */
      for (;;)
      {
	 wrefresh (menu->titleWin[menu->currentTitle]);
	 input = wgetch (menu->titleWin[menu->currentTitle]);

	 /* Inject the character into the widget. */
	 ret = injectCDKMenu (menu, input);
	 if (menu->exitType != vEARLY_EXIT)
	 {
	    return ret;
	 }
      }
   }  
   else
   {
      int count = chlen (actions);
      int x = 0;

      for (x=0; x < count; x++)
      {
	 ret = injectCDKMenu (menu, actions[x]);
	 if (menu->exitType != vEARLY_EXIT)
	 {
	    return ret;
	 }
      }
   }

   /* Set the exit type and return. */
   menu->exitType = vEARLY_EXIT;
   return -1;
}

/*
 * This injects a character into the menu widget.
 */
int injectCDKMenu (CDKMENU *menu, chtype input)
{
   int ppReturn = 1;

   /* Set the exit type. */
   menu->exitType = vEARLY_EXIT;

   /* Check if there is a pre-process function to be called. */
   if (menu->preProcessFunction != 0)
   {
      /* Call the pre-process function. */
      ppReturn = ((PROCESSFN)(menu->preProcessFunction)) (vMENU, menu, menu->preProcessData, input);
   }

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check for key bindings. */
      if (checkCDKObjectBind (vMENU, menu, input) != 0)
      {
	 return -1;
      }
      else
      {
	 switch (input)
	 {
	    case KEY_LEFT :
		 /* Erase the menu sub-window */
		 eraseCDKMenuSubwin (menu);
		 refreshCDKScreen (ScreenOf(menu));
   
		 /* Set the values. */
		 menu->currentSubtitle = 0;
		 if (menu->currentTitle == 0)
		 {
		    menu->currentTitle = menu->menuItems - 1;
		 }
		 else
		 {
		    menu->currentTitle--;
		 }
   
		 /* Draw the new menu sub-window. */
		 drawCDKMenuSubwin (menu);
		 break;
   
	    case KEY_RIGHT : case KEY_TAB :
		 /* Erase the menu sub-window. */
		 eraseCDKMenuSubwin (menu);
		 refreshCDKScreen (ScreenOf(menu));
   
		 /* Set the values. */
		 menu->currentSubtitle = 0;
		 if (menu->currentTitle == menu->menuItems-1)
		 {
		       menu->currentTitle = 0;
		 }
		 else
		 {
		    menu->currentTitle++;
		 }
   
		 /* Draw the new menu sub-window. */
		 drawCDKMenuSubwin (menu);
		 break;
   
	    case KEY_UP :
		 /* Erase the old subtitle. */
		 writeChtype (menu->pullWin[menu->currentTitle],
				1, menu->currentSubtitle+1,
				menu->sublist[menu->currentTitle][menu->currentSubtitle],
				HORIZONTAL,
				0, menu->sublistLen[menu->currentTitle][menu->currentSubtitle]);
   
		 /* Set the values. */
		 if (menu->currentSubtitle == 0)
		 {
		    menu->currentSubtitle = menu->subsize[menu->currentTitle] - 1;
		 }
		 else
		 {
		    menu->currentSubtitle--;
		 }
   
		 /* Draw the new sub-title. */
		 writeChtypeAttrib (menu->pullWin[menu->currentTitle],
				1, menu->currentSubtitle+1,
				menu->sublist[menu->currentTitle][menu->currentSubtitle],
				menu->subtitleAttr,
				HORIZONTAL,
				0, menu->sublistLen[menu->currentTitle][menu->currentSubtitle]);
		 wnoutrefresh (menu->pullWin[menu->currentTitle]);
		 break;
   
	    case KEY_DOWN : case SPACE :
		 /* Erase the old subtitle. */
		 writeChtype (menu->pullWin[menu->currentTitle],
				1, menu->currentSubtitle+1,
				menu->sublist[menu->currentTitle][menu->currentSubtitle],
				HORIZONTAL,
				0, menu->sublistLen[menu->currentTitle][menu->currentSubtitle]);
   
		 /* Set the values. */
		 if (menu->currentSubtitle == menu->subsize[menu->currentTitle]-1)
		 {
		    menu->currentSubtitle = 0;
		 }
		 else
		 {
		    menu->currentSubtitle++;
		 }
   
		 /* Draw the new sub-title. */
		 writeChtypeAttrib (menu->pullWin[menu->currentTitle],
				1, menu->currentSubtitle+1,
				menu->sublist[menu->currentTitle][menu->currentSubtitle],
				menu->subtitleAttr,
				HORIZONTAL,
				0, menu->sublistLen[menu->currentTitle][menu->currentSubtitle]);
		 wnoutrefresh (menu->pullWin[menu->currentTitle]);
		 break;
    
	    case KEY_ESC :
		 cleanUpMenu (menu);
		 menu->exitType = vESCAPE_HIT;
		 menu->lastSelection = -1;
		 return menu->lastSelection;
	
	    case KEY_RETURN : case KEY_ENTER : case KEY_CR :
		 cleanUpMenu (menu);
		 menu->exitType = vNORMAL;
		 menu->lastSelection = ((menu->currentTitle * 100) + menu->currentSubtitle);
		 return menu->lastSelection;
   
	    case CDK_REFRESH :
		 eraseCDKScreen (ScreenOf(menu));
		 refreshCDKScreen (ScreenOf(menu));
		 break;

	    default :
		 Beep();
		 break;
	 }
      }

      /* Should we call a post-process? */
      if (menu->postProcessFunction != 0)
      {
	 ((PROCESSFN)(menu->postProcessFunction)) (vMENU, menu, menu->postProcessData, input);
      }
   }

   /* Set the exit type and exit */
   menu->exitType = vEARLY_EXIT;
   return -1;
}

/*
 * This draws a menu item subwindow.
 */
void drawCDKMenuSubwin (CDKMENU *menu)
{
   int x;

   /* Box the window. */
   box (menu->pullWin[menu->currentTitle], ACS_VLINE, ACS_HLINE);
   if (menu->menuPos == BOTTOM)
   {
      mvwaddch (menu->pullWin[menu->currentTitle],
		menu->subsize[menu->currentTitle]+1, 0, ACS_LTEE);
   }
   else
   {
      mvwaddch (menu->pullWin[menu->currentTitle], 0, 0, ACS_LTEE);
   }

   /* Draw in the items. */
   for (x=0; x < menu->subsize[menu->currentTitle] ; x++)
   {
      if (x == menu->currentSubtitle)
      {
         writeChtypeAttrib (menu->pullWin[menu->currentTitle],
			1, x+1,
			menu->sublist[menu->currentTitle][x],
			menu->subtitleAttr,
			HORIZONTAL,
			0, menu->sublistLen[menu->currentTitle][x]);
      }
      else
      {
         writeChtype (menu->pullWin[menu->currentTitle],
			1, x+1,
			menu->sublist[menu->currentTitle][x],
			HORIZONTAL,
			0, menu->sublistLen[menu->currentTitle][x]);
      }
   }
   wnoutrefresh (menu->pullWin[menu->currentTitle]);

   /* Highlight the title. */
   writeChtypeAttrib (menu->titleWin[menu->currentTitle],
			0, 0, menu->title[menu->currentTitle],
			menu->titleAttr, HORIZONTAL, 0,
			menu->titleLen[menu->currentTitle]);
   wnoutrefresh (menu->titleWin[menu->currentTitle]);
}

/*
 * This erases a menu item subwindow.
 */
void eraseCDKMenuSubwin (CDKMENU *menu)
{
   eraseCursesWindow (menu->pullWin[menu->currentTitle]);

   /* Redraw the sub-menu title. */
   writeChtype (menu->titleWin[menu->currentTitle],
		0, 0, menu->title[menu->currentTitle],
		HORIZONTAL,
		0, menu->titleLen[menu->currentTitle]);
   wnoutrefresh (menu->titleWin[menu->currentTitle]);
}

/*
 * This function draws the menu.
 */
static void _drawCDKMenu (CDKOBJS *object, boolean Box GCC_UNUSED)
{
   CDKMENU *menu = (CDKMENU *)object;
   int x;

   /* Draw in the menu titles. */
   for (x=0; x < menu->menuItems; x++)
   {
      writeChtype (menu->titleWin[x], 0, 0, menu->title[x], HORIZONTAL, 0, menu->titleLen[x]);
      wnoutrefresh (menu->titleWin[x]);
   }
}

/*
 * This moves the menu to the given location.
 */
static void _moveCDKMenu (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
   CDKMENU *menu = (CDKMENU *)object;

   /*
    * If this is a relative move, then we will adjust where we want
    * to move to.
    */
   if (relative)
   {
      xplace += getbegx(WindowOf(menu));
      yplace += getbegy(WindowOf(menu));
   }

   /* Adjust the window if we need to. */
   alignxy (WindowOf(menu), &xplace, &yplace, getmaxx(WindowOf(menu)), getmaxy(WindowOf(menu)));

   /* Move the windows to the new location. */
   moveCursesWindow(WindowOf(menu), xplace, yplace);

   /* Redraw the window, if they asked for it. */
   if (refresh_flag)
   {
      drawCDKMenu (menu, ObjOf(menu)->box);
   }
}

/*
 * This sets the background color of the widget.
 */ 
void setCDKMenuBackgroundColor (CDKMENU *menu, char *color)
{
   chtype *holder = 0;
   int x, junk1, junk2;

   /* Make sure the color isn't null. */
   if (color == 0)
   {
      return;
   }

   /* Convert the value of the environment variable to a chtype. */
   holder = char2Chtype (color, &junk1, &junk2);

   /* Set the widgets background color. */
   for (x=0; x < menu->menuItems; x++)
   {
      wbkgd (menu->titleWin[x], holder[0]);
      wbkgd (menu->pullWin[x], holder[0]);
   }

   /* Clean up. */
   freeChtype (holder);
}

/*
 * This function destroys a menu widget.
 */
void destroyCDKMenu (CDKMENU *menu)
{
   int x, y;

   /* Erase the object. */
   eraseCDKMenu (menu);

   /* Clean up both the winodws and the char pointers. */
   for (x=0; x < menu->menuItems; x++)
   {
      /* Clean the windows. */
      deleteCursesWindow (menu->titleWin[x]);
      deleteCursesWindow (menu->pullWin[x]);

      /* Delete the character pointers. */
      freeChtype (menu->title[x]);
      for (y=0; y < menu->subsize[x] ; y++)
      {
	 freeChtype (menu->sublist[x][y]);
      }
   }

   /* Unregister this object. */
   unregisterCDKObject (vMENU, menu);

   /* Finish cleaning up. */
   free (menu);
}

/*
 * This function erases the menu widget from the screen.
 */
static void _eraseCDKMenu (CDKOBJS *object)
{
   CDKMENU *menu = (CDKMENU *)object;
   int x = 0;

   /* Erase the menu items. */
   for (x=0 ; x < menu->menuItems; x++)
   {
      werase (menu->titleWin[x]);
      wnoutrefresh (menu->titleWin[x]);
      werase (menu->pullWin[x]);
      wnoutrefresh (menu->pullWin[x]);
   }
}

/*
 * This sets multiple features of the menu.
 */
void setCDKMenu (CDKMENU *menu, int menuItem, int subMenuItem, chtype titleHighlight, chtype subTitleHighlight)
{
   setCDKMenuCurrentItem (menu, menuItem, subMenuItem);
   setCDKMenuTitleHighlight (menu, titleHighlight);
   setCDKMenuSubTitleHighlight (menu, subTitleHighlight);
}

/*
 * This sets the current menu itme to highlight.
 */
void setCDKMenuCurrentItem (CDKMENU *menu, int menuitem, int submenuitem)
{
   /* Set the menu item position values. */
   if (menuitem < 0)
   {
      menu->currentTitle = 0;
   }
   else if (menuitem >= menu->menuItems)
   {
      menu->currentTitle = menu->menuItems - 1;
   }
   else
   {
      menu->currentTitle = menuitem;
   }
   if (submenuitem < 0)
   {
      menu->currentSubtitle = 0;
   }
   else if (submenuitem >= menu->subsize[menu->currentTitle])
   {
      menu->currentSubtitle = menu->subsize[menu->currentTitle]-1;
   }
   else
   {
      menu->currentSubtitle = submenuitem;
   }
}
void getCDKMenuCurrentItem (CDKMENU *menu, int *menuItem, int *subMenuItem)
{
   (*menuItem)		= menu->currentTitle;
   (*subMenuItem)	= menu->currentSubtitle;
}

/*
 * This sets the attribute of the menu titles.
 */
void setCDKMenuTitleHighlight (CDKMENU *menu, chtype highlight)
{
   menu->titleAttr = highlight;
}
chtype getCDKMenuTitleHighlight (CDKMENU *menu)
{
   return menu->titleAttr;
}

/*
 * This sets the attribute of the sub-title.
 */
void setCDKMenuSubTitleHighlight (CDKMENU *menu, chtype highlight)
{
   menu->subtitleAttr = highlight;
}
chtype getCDKMenuSubTitleHighlight (CDKMENU *menu)
{
   return menu->subtitleAttr;
}

/* 
 * This exits the menu.
 */
static void cleanUpMenu (CDKMENU *menu)
{
   /* Erase the sub-menu. */
   eraseCDKMenuSubwin (menu);
   wnoutrefresh (menu->pullWin[menu->currentTitle]);

   /* Refresh the screen. */
   refreshCDKScreen (ScreenOf(menu));
}

/*
 * This function sets the pre-process function.
 */
void setCDKMenuPreProcess (CDKMENU *menu, PROCESSFN callback, void *data)
{
   menu->preProcessFunction = callback;
   menu->preProcessData = data;
}
 
/*
 * This function sets the post-process function.
 */
void setCDKMenuPostProcess (CDKMENU *menu, PROCESSFN callback, void *data)
{
   menu->postProcessFunction = callback;
   menu->postProcessData = data;
}