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

File: [cvs.NetBSD.org] / othersrc / dist / cdk / radio.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 drawCDKRadioList (CDKRADIO *radio);
static void createCDKRadioItemList (CDKRADIO *radio,
				char **list, int listSize);

DeclareCDKObjects(my_funcs,Radio);

/*
 * This function creates the radio widget.
 */
CDKRADIO *newCDKRadio (CDKSCREEN *cdkscreen, int xplace, int yplace, int splace, int height, int width, char *title, char **list, int listSize, chtype choiceChar, int defItem, chtype highlight, boolean Box, boolean shadow)
{
   /* Declare local variables. */
   CDKRADIO *radio	= newCDKObject(CDKRADIO, &my_funcs);
   chtype *holder	= 0;
   int parentWidth	= getmaxx(cdkscreen->window);
   int parentHeight	= getmaxy(cdkscreen->window);
   int boxWidth		= width;
   int boxHeight	= height;
   int maxWidth		= INT_MIN;
   int xpos		= xplace;
   int ypos		= yplace;
   char **temp		= 0;
   int x, len, junk2;

   if (splace == LEFT || splace == RIGHT)
   {
      radio->scrollbar = TRUE;
   }
   else
   {
      radio->scrollbar = FALSE;
   }

  /*
   * If the height is a negative value, the height will
   * be ROWS-height, otherwise, the height will be the
   * given height.
   */
   boxHeight = setWidgetDimension (parentHeight, height, 0);

  /*
   * If the width is a negative value, the width will
   * be COLS-width, otherwise, the width will be the
   * given width.
   */
   boxWidth = setWidgetDimension (parentWidth, width, 5);

   /* Translate the char * title to a chtype * */
   if (title != 0)
   {
      temp = CDKsplitString (title, '\n');
      radio->titleLines = CDKcountStrings (temp);

      /* We need to determine the widest title line. */
      for (x=0; x < radio->titleLines; x++)
      {
	 holder = char2Chtype (temp[x], &len, &junk2);
	 maxWidth = MAXIMUM (maxWidth, len);
	 freeChtype (holder);
      }
      boxWidth = MAXIMUM (boxWidth, maxWidth + 2);

      /* For each line in the title, convert from a char * to a chtype * */
      for (x=0; x < radio->titleLines; x++)
      {
	 radio->title[x]	= char2Chtype (temp[x],
					&radio->titleLen[x],
					&radio->titlePos[x]);
	 radio->titlePos[x]	= justifyString (boxWidth - 2,
					radio->titleLen[x],
					radio->titlePos[x]);
      }

      CDKfreeStrings(temp);
   }
   else
   {
      radio->titleLines = 0;
   }

   /* Set the box height. */
   if (radio->titleLines > boxHeight)
   {
      if (listSize > 8)
      {
	 boxHeight = radio->titleLines + 10;
      }
      else
      {
	 boxHeight = radio->titleLines + listSize + 2;
      }
   }
   radio->fieldWidth	= boxWidth - 2 - radio->scrollbar;
   radio->viewSize	= boxHeight - 2 - radio->titleLines;

   /*
    * Make sure we didn't extend beyond the dimensions of the window.
    */
   boxWidth = MINIMUM (boxWidth, parentWidth);
   boxHeight = MINIMUM (boxHeight, parentHeight);

   /* Rejustify the x and y positions if we need to. */
   alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight);

   /* Make the radio window */
   radio->win = newwin (boxHeight + !!shadow, boxWidth + !!shadow, ypos, xpos);

   /* Is the window null??? */
   if (radio->win == 0)
   {
      /* Clean up memory. */
      for (x=0; x < radio->titleLines; x++)
      {
	 freeChtype (radio->title[x]);
      }
      free (radio);

      /* Return a null pointer. */
      return (0);
   }
   keypad (radio->win, TRUE);
   leaveok (radio->win, TRUE);

   if (radio->titleLines > 0)
   {
      /* Make the title window. */
      radio->titleWin = subwin (radio->win,
				radio->titleLines, boxWidth - 2,
				ypos + 1, xpos + 1);
   }

   /* Create the scrollbar window. */
   if (splace == RIGHT)
   {
      radio->fieldWin = subwin (radio->win,
				radio->viewSize, radio->fieldWidth,
				ypos + radio->titleLines + 1,
				xpos + 1);
      radio->scrollbarWin = subwin (radio->win,
				radio->viewSize, 1,
				ypos + radio->titleLines + 1,
				xpos + 1 + radio->fieldWidth);
   }
   else if (splace == LEFT)
   {
      radio->scrollbarWin = subwin (radio->win,
				radio->viewSize, 1,
				ypos + radio->titleLines + 1,
				xpos + 1);
      radio->fieldWin = subwin (radio->win,
				radio->viewSize, radio->fieldWidth,
				ypos + radio->titleLines + 1,
				xpos + 2);
   }
   else
   {
      radio->fieldWin = subwin (radio->win,
				radio->viewSize, radio->fieldWidth,
				ypos + radio->titleLines + 1,
				xpos + 1);
      radio->scrollbarWin = 0;
   }

   /* Set the rest of the variables */
   ScreenOf(radio)		= cdkscreen;
   radio->parent		= cdkscreen->window;
   radio->boxHeight		= boxHeight;
   radio->boxWidth		= boxWidth;
   radio->currentTop		= 0;
   radio->currentItem		= 0;
   radio->currentHigh		= 0;
   radio->leftChar		= 0;
   radio->selectedItem		= defItem;
   radio->highlight		= highlight;
   radio->choiceChar		= choiceChar;
   radio->leftBoxChar		= (chtype)'[';
   radio->rightBoxChar		= (chtype)']';
   radio->exitType		= vNEVER_ACTIVATED;
   ObjOf(radio)->box		= Box;
   radio->shadow		= shadow;
   radio->preProcessFunction	= 0;
   radio->preProcessData	= 0;
   radio->postProcessFunction	= 0;
   radio->postProcessData	= 0;
   radio->ULChar		= ACS_ULCORNER;
   radio->URChar		= ACS_URCORNER;
   radio->LLChar		= ACS_LLCORNER;
   radio->LRChar		= ACS_LRCORNER;
   radio->HChar			= ACS_HLINE;
   radio->VChar			= ACS_VLINE;
   radio->BoxAttrib		= A_NORMAL;

   /* Create the scrolling list item list and needed variables. */
   createCDKRadioItemList (radio, list, listSize);

   /* Clean the key bindings. */
   cleanCDKObjectBindings (vRADIO, radio);

   /* Register this baby. */
   registerCDKObject (cdkscreen, vRADIO, radio);

   /* Return the radio list */
   return (radio);
}

/*
 * This actually manages the radio widget.
 */
int activateCDKRadio (CDKRADIO *radio, chtype *actions)
{
   /* Draw the radio list. */
   drawCDKRadio (radio, ObjOf(radio)->box);

   /* Check if actions is null. */
   if (actions == 0)
   {
      /* Declare some local variables. */
      chtype input;
      int ret;

      for (;;)
      {
	 /* Get the input. */
	 wrefresh (radio->win);
	 input = wgetch (radio->win);

	 /* Inject the character into the widget. */
	 ret = injectCDKRadio (radio, input);
	 if (radio->exitType != vEARLY_EXIT)
	 {
	    return ret;
	 }
      }
   }
   else
   {
      /* Declare some local variables. */
      int length = chlen (actions);
      int x, ret;

      /* Inject each character one at a time. */
      for (x=0; x < length; x++)
      {
	 ret = injectCDKRadio (radio, actions[x]);
	 if (radio->exitType != vEARLY_EXIT)
	 {
	    return ret;
	 }
      }
   }

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

/*
 * This injects a single character into the widget.
 */
int injectCDKRadio (CDKRADIO *radio, chtype input)
{
   /* Declare local variables. */
   int ppReturn = 1;

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

   /* Draw the radio list */
   drawCDKRadioList (radio);

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

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check for a predefined key binding. */
      if (checkCDKObjectBind (vRADIO, radio, input) != 0)
      {
	 return -1;
      }
      else
      {
	 switch (input)
	 {
	    case KEY_UP :
		 if (radio->currentItem > 0)
		 {
		    radio->currentItem--;
		    if (radio->currentHigh == 0)
		    {
		       radio->currentTop--;
		    }
		    else
		    {
		       radio->currentHigh--;
		    }
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case KEY_DOWN :
		 if (radio->currentItem < radio->listSize - 1)
		 {
		    radio->currentItem++;
		    if (radio->currentHigh == radio->viewSize - 1)
		    {
		       radio->currentTop++;
		    }
		    else
		    {
		       radio->currentHigh++;
		    }
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case KEY_LEFT :
		 if (radio->leftChar > 0)
		 {
		    radio->leftChar--;
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case KEY_RIGHT :
		 if (radio->leftChar < radio->maxLeftChar)
		 {
		    radio->leftChar++;
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case KEY_PPAGE :
	    case CONTROL('B') :
		 if (radio->currentItem > 0)
		 {
		    radio->currentTop =
			MAXIMUM (radio->currentTop - radio->viewSize + 1,
				 0);
		    radio->currentItem =
			MAXIMUM (radio->currentItem - radio->viewSize + 1,
				 0);
		    radio->currentHigh =
			radio->currentItem - radio->currentTop;
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case KEY_NPAGE :
	    case CONTROL('F') :
		 if (radio->currentItem < radio->listSize - 1)
		 {
		    radio->currentTop =
			MINIMUM (radio->currentTop + radio->viewSize - 1,
				 radio->maxTopItem);
		    radio->currentItem =
			MINIMUM (radio->currentItem + radio->viewSize - 1,
				 radio->listSize-1);
		    radio->currentHigh =
			radio->currentItem - radio->currentTop;
		 }
		 else
		 {
		    Beep();
		 }
		 break;

	    case 'g' :
	    case '1' :
	    case KEY_HOME :
		 radio->currentTop	= 0;
		 radio->currentItem	= 0;
		 radio->currentHigh	= 0;
		 break;

	    case 'G' :
	    case KEY_END :
		 radio->currentTop	= radio->maxTopItem;
		 radio->currentItem	= radio->listSize-1;
		 radio->currentHigh	= radio->currentItem - radio->currentTop;
		 break;

	    case '|' :
		 radio->leftChar	= 0;
		 break;

	    case '$' :
		 radio->leftChar	= radio->maxLeftChar;
		 break;

	    case SPACE :
		 radio->selectedItem	= radio->currentItem;
		 break;

	    case KEY_ESC :
		 radio->exitType = vESCAPE_HIT;
		 return -1;

	    case KEY_RETURN :
	    case KEY_TAB :
	    case KEY_ENTER :
	    case KEY_CR :
		 radio->exitType = vNORMAL;
		 return radio->selectedItem;

	    case CDK_REFRESH :
		 eraseCDKScreen (ScreenOf(radio));
		 refreshCDKScreen (ScreenOf(radio));
		 break;

	    default :
		 Beep();
		 break;
	 }
      }

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

   /* Draw the radio list */
   drawCDKRadioList (radio);

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

/*
 * This moves the radio field to the given location.
 */
static void _moveCDKRadio (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
   CDKRADIO *radio = (CDKRADIO *)object;

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

   /* Adjust the window if we need to. */
   alignxy (WindowOf(radio), &xplace, &yplace, radio->boxWidth, radio->boxHeight);

   /* Move the window to the new location. */
   moveCursesWindow(radio->win, xplace, yplace);

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

/*
 * This function draws the radio widget.
 */
static void _drawCDKRadio (CDKOBJS *object, boolean Box GCC_UNUSED)
{
   CDKRADIO *radio = (CDKRADIO *)object;
   int x;

   /* Box it if needed. */
   if (Box)
   {
      attrbox (radio->win,
		radio->ULChar, radio->URChar,
		radio->LLChar, radio->LRChar,
		radio->HChar,  radio->VChar,
		radio->BoxAttrib,
		radio->shadow);
   }

   if (radio->titleLines > 0)
   {
      /* Draw in the title if there is one */
      for (x=0; x < radio->titleLines; x++)
      {
	 writeChtype (radio->titleWin,
			radio->titlePos[x], x,
			radio->title[x],
			HORIZONTAL, 0,
			radio->titleLen[x]);
      }
      wnoutrefresh (radio->titleWin);
   }

   /* Draw in the radio list. */
   drawCDKRadioList (radio);
}

/*
 * This redraws the radio list.
 */
static void drawCDKRadioList (CDKRADIO *radio)
{
   /* Declare local variables. */
   int screenPos, x, y;
   chtype temp[3];

   werase (radio->fieldWin);

   /* Redraw the list */
   for (x=0, y=radio->currentTop; x < radio->viewSize; x++, y++)
   {
      if (y >= radio->listSize)
      {
	 break;
      }

      screenPos = radio->itemPos[y] - radio->leftChar;

      /* Draw in the line. */
      if (y == radio->currentItem)
      {
	 if (screenPos >= 0)
	 {
	    writeChtypeAttrib (radio->fieldWin,
			screenPos, x,
			radio->item[y],
			radio->highlight,
			HORIZONTAL, 0,
			radio->itemLen[y]);
	 }
	 else
	 {
	    writeChtypeAttrib (radio->fieldWin,
			0, x,
			radio->item[y],
			radio->highlight,
			HORIZONTAL, -screenPos,
			radio->itemLen[y]);
	 }
      }
      else
      {
	 if (screenPos >= 0)
	 {
	    writeChtype (radio->fieldWin,
			screenPos, x,
			radio->item[y],
			HORIZONTAL, 0,
			radio->itemLen[y]);
	 }
	 else
	 {
	    writeChtype (radio->fieldWin,
			0, x,
			radio->item[y],
			HORIZONTAL, -screenPos,
			radio->itemLen[y]);
	 }
      }

      screenPos = - radio->leftChar;

      temp[0] = radio->leftBoxChar;
      if (y == radio->selectedItem)
      {
         temp[1] = radio->choiceChar;
      }
      else
      {
         temp[1] = ' ';
      }
      temp[2] = radio->rightBoxChar;

      writeChtype (radio->fieldWin,
		0, x,
		temp,
		HORIZONTAL, -screenPos,
		3);
   }

   if (radio->scrollbar)
   {
      int togglePos;

      /* Determine where the toggle is supposed to be. */
      if (radio->listSize >= radio->viewSize)
	 togglePos = floorCDK ((float)radio->viewSize * (float)radio->currentItem / (float)radio->listSize);
      else
	 togglePos = floorCDK ((float)(radio->viewSize - radio->toggleSize) * (float)radio->currentItem / (float)(radio->listSize - 1));

      /* Make sure the toggle button doesn't go out of bounds. */
      togglePos = MINIMUM (togglePos, radio->viewSize - radio->toggleSize);

      /* Draw the scrollbar. */
      mvwvline (radio->scrollbarWin, 0, 0, ACS_CKBOARD, radio->viewSize);
      mvwvline (radio->scrollbarWin, togglePos, 0, ' ' | A_REVERSE, radio->toggleSize);
      wnoutrefresh (radio->scrollbarWin);
   }

   /* Refresh the window. */
   wnoutrefresh (radio->fieldWin);
   wnoutrefresh (radio->win);
}

/*
 * These functions set the drawing characters of the widget.
 */
void setCDKRadioULChar (CDKRADIO *radio, chtype character)
{
   radio->ULChar = character;
}
void setCDKRadioURChar (CDKRADIO *radio, chtype character)
{
   radio->URChar = character;
}
void setCDKRadioLLChar (CDKRADIO *radio, chtype character)
{
   radio->LLChar = character;
}
void setCDKRadioLRChar (CDKRADIO *radio, chtype character)
{
   radio->LRChar = character;
}
void setCDKRadioVerticalChar (CDKRADIO *radio, chtype character)
{
   radio->VChar = character;
}
void setCDKRadioHorizontalChar (CDKRADIO *radio, chtype character)
{
   radio->HChar = character;
}
void setCDKRadioBoxAttribute (CDKRADIO *radio, chtype character)
{
   radio->BoxAttrib = character;
}

/*
 * This sets the background color of the widget.
 */
void setCDKRadioBackgroundColor (CDKRADIO *radio, char *color)
{
   chtype *holder = 0;
   int 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. */
   wbkgd (radio->win, holder[0]);
   if (radio->scrollbar)
   {
      wbkgd (radio->scrollbarWin, holder[0]);
   }

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

/*
 * This function destroys the radio widget.
 */
void destroyCDKRadio (CDKRADIO *radio)
{
   /* Declare local variables. */
   int x;

   /* Erase the object. */
   eraseCDKRadio (radio);

   /* Clear up the char pointers. */
   for (x=0; x < radio->titleLines; x++)
   {
      freeChtype (radio->title[x]);
   }
   for (x=0; x < radio->listSize; x++)
   {
      freeChtype (radio->item[x]);
   }

   /* Clean up the windows. */
   deleteCursesWindow (radio->win);

   /* Unregister this object. */
   unregisterCDKObject (vRADIO, radio);

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

/*
 * This function erases the radio widget.
 */
static void _eraseCDKRadio (CDKOBJS *object)
{
   CDKRADIO *radio = (CDKRADIO *)object;

   eraseCursesWindow (radio->win);
}

/*
 * This function creates massages the scrolling list information and
 * sets up the needed variables for the scrolling list to work correctly.
 */
static void createCDKRadioItemList (CDKRADIO *radio, char **list, int listSize)
{
   /* Declare local variables. */
   int widestItem, x;

   /* Is the view size larger than the list? */
   radio->listSize	= listSize;
   radio->maxTopItem	= MAXIMUM (0, listSize - radio->viewSize);

   /* Determine the size of the scrollbar toggle and the step. */
   if (listSize >= radio->viewSize)
      radio->toggleSize = 1;
   else
      radio->toggleSize = floorCDK ((float)radio->viewSize / (float)listSize + 0.5);

   /* Each item in the needs to be converted to chtype * */
   widestItem		= 0;
   for (x=0; x < listSize; x++)
   {
      radio->item[x]	= char2Chtype (list[x], &radio->itemLen[x], &radio->itemPos[x]);
      radio->itemPos[x] = justifyString (radio->fieldWidth, radio->itemLen[x], radio->itemPos[x]) + 3;
      widestItem	= MAXIMUM (widestItem, radio->itemLen[x]);
   }

   /*
    * Determine how many characters we can shift to the right
    * before all the items have been scrolled off the screen.
    */
   radio->maxLeftChar = MAXIMUM (0, widestItem - radio->fieldWidth + 3);
}

/*
 * This set various attributes of the radio list.
 */
void setCDKRadio (CDKRADIO *radio, chtype highlight, chtype choiceChar, int Box)
{
   setCDKRadioChoiceCharacter (radio, choiceChar);
   setCDKRadioHighlight (radio, highlight);
   setCDKRadioBox (radio, Box);
}

/*
 * This sets the radio list items.
 */
void setCDKRadioItems (CDKRADIO *radio, char **list, int listSize)
{
   /* Declare some wars. */
   int x;

   /* Clean out the old list. */
   for (x=0; x < radio->listSize; x++)
   {
      freeChtype (radio->item[x]);
      radio->itemLen[x] = 0;
      radio->itemPos[x] = 0;
   }

   /* Set some vars. */
   radio->currentTop	= 0;
   radio->currentItem	= 0;
   radio->currentHigh	= 0;
   radio->leftChar	= 0;
   radio->selectedItem	= 0;

   /* Set up the new list. */
   createCDKRadioItemList (radio, list, listSize);
}
int getCDKRadioItems (CDKRADIO *radio, char *list[])
{
   int x;

   for (x=0; x < radio->listSize; x++)
   {
      list[x] = chtype2Char (radio->item[x]);
   }
   return radio->listSize;
}

/*
 * This sets the highlight bar of the radio list.
 */
void setCDKRadioHighlight (CDKRADIO *radio, chtype highlight)
{
   radio->highlight = highlight;
}
chtype getCDKRadioHighlight (CDKRADIO *radio)
{
   return radio->highlight;
}

/*
 * This sets the character to use when selecting an item in the list.
 */
void setCDKRadioChoiceCharacter (CDKRADIO *radio, chtype character)
{
   radio->choiceChar = character;
}
chtype getCDKRadioChoiceCharacter (CDKRADIO *radio)
{
   return radio->choiceChar;
}

/*
 * This sets the character to use to draw the left side of the
 * choice box on the list.
 */
void setCDKRadioLeftBrace (CDKRADIO *radio, chtype character)
{
   radio->leftBoxChar = character;
}
chtype getCDKRadioLeftBrace (CDKRADIO *radio)
{
   return radio->leftBoxChar;
}

/*
 * This sets the character to use to draw the right side of the
 * choice box on the list.
 */
void setCDKRadioRightBrace (CDKRADIO *radio, chtype character)
{
   radio->rightBoxChar = character;
}
chtype getCDKRadioRightBrace (CDKRADIO *radio)
{
   return radio->rightBoxChar;
}

/*
 * This sets the box attribute of the widget.
 */
void setCDKRadioBox (CDKRADIO *radio, boolean Box)
{
   ObjOf(radio)->box = Box;
}
boolean getCDKRadioBox (CDKRADIO *radio)
{
   return ObjOf(radio)->box;
}

/*
 * This function sets the pre-process function.
 */
void setCDKRadioPreProcess (CDKRADIO *radio, PROCESSFN callback, void *data)
{
   radio->preProcessFunction = callback;
   radio->preProcessData = data;
}

/*
 * This function sets the post-process function.
 */
void setCDKRadioPostProcess (CDKRADIO *radio, PROCESSFN callback, void *data)
{
   radio->postProcessFunction = callback;
   radio->postProcessData = data;
}