#include <cdk_int.h>

/*
 * $Author: tom $
 * $Date: 2004/08/31 23:26:45 $
 * $Revision: 1.73 $
 */

static int createList (CDKITEMLIST *itemlist, char **item, int count);

DeclareCDKObjects(ITEMLIST, Itemlist, setCdk, Int);

/*
 * This creates a pointer to an itemlist widget.
 */
CDKITEMLIST *newCDKItemlist (CDKSCREEN *cdkscreen, int xplace, int yplace, char *title, char *label, char **item, int count, int defaultItem, boolean Box, boolean shadow)
{
   /* Set up some variables.  */
   CDKITEMLIST *itemlist = 0;
   int parentWidth	= getmaxx(cdkscreen->window);
   int parentHeight	= getmaxy(cdkscreen->window);
   int boxWidth		= 0;
   int boxHeight;
   int maxWidth		= INT_MIN;
   int fieldWidth	= 0;
   int xpos		= xplace;
   int ypos		= yplace;
   int x, junk;

   if ((itemlist = newCDKObject(CDKITEMLIST, &my_funcs)) == 0
    || !createList(itemlist, item, count))
   {
      destroyCDKObject (itemlist);
      return (0);
   }

   setCDKItemlistBox (itemlist, Box);
   boxHeight		= (BorderOf(itemlist) * 2) + 1;

   /* Set some basic values of the itemlist. */
   itemlist->label	= 0;
   itemlist->labelLen	= 0;
   itemlist->labelWin	= 0;

   /* Translate the label char *pointer to a chtype pointer. */
   if (label != 0)
   {
      itemlist->label = char2Chtype (label, &itemlist->labelLen, &junk);
   }

   /* Go through the list and determine the widest item. */
   for (x=0; x < count; x++)
   {
      maxWidth = MAXIMUM (maxWidth, itemlist->itemLen[x]);
   }

   /*
    * Set the box width.  Allow an extra char in field width for cursor
    */
   fieldWidth = maxWidth + 1;
   boxWidth = fieldWidth + itemlist->labelLen + 2 * BorderOf(itemlist);

   boxWidth = setCdkTitle(ObjOf(itemlist), title, boxWidth);

   boxHeight += TitleLinesOf(itemlist);

  /*
   * Make sure we didn't extend beyond the dimensions of the window.
   */
   boxWidth = (boxWidth > parentWidth ? parentWidth : boxWidth);
   boxHeight = (boxHeight > parentHeight ? parentHeight : boxHeight);
   fieldWidth = MINIMUM(fieldWidth, boxWidth - itemlist->labelLen - 2 * BorderOf(itemlist));

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

   /* Make the window. */
   itemlist->win = newwin(boxHeight, boxWidth, ypos, xpos);
   if (itemlist->win == 0)
   {
      destroyCDKObject (itemlist);
      return (0);
   }

   /* Make the label window if there was a label. */
   if (itemlist->label != 0)
   {
      itemlist->labelWin = subwin (itemlist->win,
				   1,
				   itemlist->labelLen,
				   ypos + BorderOf(itemlist) + TitleLinesOf(itemlist),
				   xpos + BorderOf(itemlist));
      if (itemlist->labelWin == 0)
      {
	 destroyCDKObject (itemlist);
	 return (0);
      }
   }

   keypad (itemlist->win, TRUE);

   /* Make the field window */
   itemlist->fieldWin = subwin (itemlist->win,
				1,
				fieldWidth,
				ypos + BorderOf(itemlist) + TitleLinesOf(itemlist),
				xpos + itemlist->labelLen + BorderOf(itemlist));
   if (itemlist->fieldWin == 0)
   {
      destroyCDKObject (itemlist);
      return (0);
   }

   keypad (itemlist->fieldWin, TRUE);

   /* Set up the rest of the structure. */
   ScreenOf(itemlist)			= cdkscreen;
   itemlist->parent			= cdkscreen->window;
   itemlist->shadowWin			= 0;
   itemlist->boxHeight			= boxHeight;
   itemlist->boxWidth			= boxWidth;
   itemlist->fieldWidth			= fieldWidth;
   itemlist->listSize			= count;
   initExitType(itemlist);
   ObjOf(itemlist)->acceptsFocus	= TRUE;
   ObjOf(itemlist)->inputWindow		= itemlist->fieldWin;
   itemlist->shadow			= shadow;

   setCDKItemlistBox (itemlist, Box);

   /* Set then default item. */
   if (defaultItem >= 0 && defaultItem < itemlist->listSize)
   {
      itemlist->currentItem	= defaultItem;
      itemlist->defaultItem	= defaultItem;
   }
   else
   {
      itemlist->currentItem	= 0;
      itemlist->defaultItem	= 0;
   }

   /* Do we want a shadow??? */
   if (shadow)
   {
      itemlist->shadowWin = newwin (boxHeight, boxWidth, ypos + 1, xpos + 1);
      if (itemlist->shadowWin == 0)
      {
	 destroyCDKObject (itemlist);
	 return (0);
      }
   }

   /* Clean the key bindings. */
   cleanCDKObjectBindings (vITEMLIST, itemlist);

   /* Register this baby.		 */
   registerCDKObject (cdkscreen, vITEMLIST, itemlist);

   /* Return the pointer to the structure */
   return (itemlist);
}

/*
 * This allows the user to play with the widget.
 */
int activateCDKItemlist (CDKITEMLIST *itemlist, chtype *actions)
{
   /* Declare local variables. */
   int ret = -1;

   /* Draw the widget. */
   drawCDKItemlist (itemlist, ObjOf(itemlist)->box);
   drawCDKItemlistField(itemlist, TRUE);

   /* Check if actions is null. */
   if (actions == 0)
   {
      chtype input = 0;
      for (;;)
      {
	 /* Get the input. */
	 input = getcCDKObject (ObjOf(itemlist));

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

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

   /* Set the exit type and exit. */
   setExitType(itemlist, 0);
   return ret;
}

/*
 * This injects a single character into the widget.
 */
static int _injectCDKItemlist (CDKOBJS *object, chtype input)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;
   /* Declare local variables. */
   int ppReturn = 1;
   int ret = unknownInt;
   bool complete = FALSE;

   /* Set the exit type. */
   setExitType(itemlist, 0);

   /* Draw the itemlist field. */
   drawCDKItemlistField (itemlist,TRUE);

   /* Check if there is a pre-process function to be called. */
   if (PreProcessFuncOf(itemlist) != 0)
   {
      /* Call the pre-process function. */
      ppReturn = PreProcessFuncOf(itemlist) (vITEMLIST, itemlist, PreProcessDataOf(itemlist), input);
   }

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check a predefined binding. */
      if (checkCDKObjectBind (vITEMLIST, itemlist, input) != 0)
      {
	 checkEarlyExit(itemlist);
	 complete = TRUE;
      }
      else
      {
	 switch (input)
	 {
	    case KEY_UP : case KEY_RIGHT : case SPACE : case '+' : case 'n' :
		 if (itemlist->currentItem < itemlist->listSize - 1)
		 {
		    itemlist->currentItem++;
		 }
		 else
		 {
		    itemlist->currentItem = 0;
		 }
		 break;

	    case KEY_DOWN : case KEY_LEFT : case '-' : case 'p' :
		 if (itemlist->currentItem > 0)
		 {
		    itemlist->currentItem--;
		 }
		 else
		 {
		    itemlist->currentItem = itemlist->listSize - 1;
		 }
		 break;

	    case 'd' : case 'D' :
		 itemlist->currentItem = itemlist->defaultItem;
		 break;

	    case '0' :
		 itemlist->currentItem = 0;
		 break;

	    case '$' :
		 itemlist->currentItem = itemlist->listSize - 1;
		 break;

	    case KEY_ESC :
		 setExitType(itemlist, input);
		 complete = TRUE;
		 break;

	    case KEY_TAB : case KEY_ENTER :
		 setExitType(itemlist, input);
		 ret = itemlist->currentItem;
		 complete = TRUE;
		 break;

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

	    default :
		 Beep();
		 break;
	 }
      }

      /* Should we call a post-process? */
      if (!complete && (PostProcessFuncOf(itemlist) != 0))
      {
	 PostProcessFuncOf(itemlist) (vITEMLIST, itemlist, PostProcessDataOf(itemlist), input);
      }
   }

   if (!complete) {
      drawCDKItemlistField (itemlist,TRUE);
      setExitType(itemlist, 0);
   }

   ResultOf(itemlist).valueInt = ret;
   return (ret != unknownInt);
}

/*
 * This moves the itemlist field to the given location.
 */
static void _moveCDKItemlist (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;
   /* Declare local variables. */
   int currentX = getbegx(itemlist->win);
   int currentY = getbegy(itemlist->win);
   int xpos	= xplace;
   int ypos	= yplace;
   int xdiff	= 0;
   int ydiff	= 0;

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

   /* Adjust the window if we need to. */
   alignxy (WindowOf(itemlist), &xpos, &ypos, itemlist->boxWidth, itemlist->boxHeight);

   /* Get the difference. */
   xdiff = currentX - xpos;
   ydiff = currentY - ypos;

   /* Move the window to the new location. */
   moveCursesWindow(itemlist->win, -xdiff, -ydiff);
   moveCursesWindow(itemlist->fieldWin, -xdiff, -ydiff);
   moveCursesWindow(itemlist->labelWin, -xdiff, -ydiff);
   moveCursesWindow(itemlist->shadowWin, -xdiff, -ydiff);

   /* Touch the windows so they 'move'. */
   refreshCDKWindow (WindowOf(itemlist));

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

/*
 * This draws the widget on the screen.
 */
static void _drawCDKItemlist (CDKOBJS *object, int Box)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

   /* Did we ask for a shadow? */
   if (itemlist->shadowWin != 0)
   {
      drawShadow (itemlist->shadowWin);
   }

   /* Box the widget if asked. */
   if (Box)
   {
      drawObjBox (itemlist->win, ObjOf(itemlist));
   }

   drawCdkTitle (itemlist->win, object);

   /* Draw in the label to the widget. */
   if (itemlist->labelWin != 0)
   {
      writeChtype (itemlist->labelWin,
		0,
		0,
		itemlist->label,
		HORIZONTAL,
		0,
		chlen(itemlist->label));
   }

   refreshCDKWindow (itemlist->win);

   /* Draw in the field. */
   drawCDKItemlistField(itemlist, FALSE);
}

/*
 * This sets the background attribute of the widget.
 */
static void _setBKattrItemlist (CDKOBJS *object, chtype attrib)
{
   if (object != 0)
   {
      CDKITEMLIST *widget = (CDKITEMLIST *) object;

      wbkgd (widget->win, attrib);
      wbkgd (widget->fieldWin, attrib);
      if (widget->labelWin != 0)
      {
	 wbkgd (widget->labelWin, attrib);
      }
   }
}

/*
 * This function draws the contents of the field.
 */
void drawCDKItemlistField (CDKITEMLIST *itemlist, boolean highlight)
{
   /* Declare local vars. */
   int currentItem = itemlist->currentItem;
   int len;
   int x;

   /* Determine how much we have to draw. */
   len = MINIMUM (itemlist->itemLen[currentItem], itemlist->fieldWidth);

   /* Erase the field window. */
   werase (itemlist->fieldWin);

   /* Draw in the current item in the field. */
   for (x=0; x < len; x++)
   {
      chtype c = itemlist->item[currentItem][x];

      if (highlight)
      {
	c = CharOf(c) | A_REVERSE;
      }

      mvwaddch (itemlist->fieldWin, 0,
		x + itemlist->itemPos[currentItem],
		c);
   }

   /* Redraw the field window. */
   refreshCDKWindow (itemlist->fieldWin);
}

/*
 * This function removes the widget from the screen.
 */
static void _eraseCDKItemlist (CDKOBJS *object)
{
   if (validCDKObject (object))
   {
      CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

      eraseCursesWindow (itemlist->fieldWin);
      eraseCursesWindow (itemlist->labelWin);
      eraseCursesWindow (itemlist->win);
      eraseCursesWindow (itemlist->shadowWin);
   }
}

/*
 * This function destroys the widget and all the memory it used.
 */
static void _destroyCDKItemlist (CDKOBJS *object)
{
   if (object != 0)
   {
      CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

      cleanCdkTitle (object);
      freeChtype (itemlist->label);
      CDKfreeChtypes (itemlist->item);

      /* Delete the windows. */
      deleteCursesWindow (itemlist->fieldWin);
      deleteCursesWindow (itemlist->labelWin);
      deleteCursesWindow (itemlist->shadowWin);
      deleteCursesWindow (itemlist->win);

      /* Unregister this object. */
      unregisterCDKObject (vITEMLIST, itemlist);
   }
}

/*
 * This sets multiple attributes of the widget.
 */
void setCDKItemlist (CDKITEMLIST *itemlist, char **list, int count, int current, boolean Box)
{
   setCDKItemlistValues (itemlist, list, count, current);
   setCDKItemlistBox (itemlist, Box);
}

/*
 * This function sets the contents of the list.
 */
void setCDKItemlistValues (CDKITEMLIST *itemlist, char **item, int count, int defaultItem)
{
   if (createList(itemlist, item, count))
   {
      /* Set the default item. */
      if ((defaultItem >= 0) && (defaultItem < itemlist->listSize))
      {
	 itemlist->currentItem = defaultItem;
	 itemlist->defaultItem = defaultItem;
      }

      /* Draw the field. */
      eraseCDKItemlist (itemlist);
      drawCDKItemlist (itemlist, ObjOf(itemlist)->box);
   }
}
chtype **getCDKItemlistValues (CDKITEMLIST *itemlist, int *size)
{
   (*size) = itemlist->listSize;
   return itemlist->item;
}

/*
 * This sets the default/current item of the itemlist.
 */
void setCDKItemlistCurrentItem (CDKITEMLIST *itemlist, int currentItem)
{
   /* Set the default item. */
   if ((currentItem >= 0) && (currentItem < itemlist->listSize))
   {
      itemlist->currentItem = currentItem;
   }
}
int getCDKItemlistCurrentItem (CDKITEMLIST *itemlist)
{
   return itemlist->currentItem;
}

/*
 * This sets the default item in the list.
 */
void setCDKItemlistDefaultItem (CDKITEMLIST *itemlist, int defaultItem)
{
   /* Make sure the item is in the correct range. */
   if (defaultItem < 0)
   {
      itemlist->defaultItem = 0;
   }
   else if (defaultItem >= itemlist->listSize)
   {
      itemlist->defaultItem = itemlist->listSize - 1;
   }
   else
   {
      itemlist->defaultItem = defaultItem;
   }
}
int getCDKItemlistDefaultItem (CDKITEMLIST *itemlist)
{
   return itemlist->defaultItem;
}

/*
 * This sets the box attribute of the itemlist widget.
 */
void setCDKItemlistBox (CDKITEMLIST *itemlist, boolean Box)
{
   ObjOf(itemlist)->box = Box;
   ObjOf(itemlist)->borderSize = Box ? 1 : 0;
}
boolean getCDKItemlistBox (CDKITEMLIST *itemlist)
{
   return ObjOf(itemlist)->box;
}

static void _focusCDKItemlist(CDKOBJS *object)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

   drawCDKItemlistField (itemlist,TRUE);
}

static void _unfocusCDKItemlist(CDKOBJS *object)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

   drawCDKItemlistField (itemlist,FALSE);
}

#if 0
static void _refreshDataCDKItemlist(CDKOBJS *object)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

   if (ReturnOf(itemlist))
   {
      switch (DataTypeOf(itemlist))
      {
      default:
      case DataTypeInt:
	 itemlist->currentItem = *((int*)ReturnOf(itemlist));
	 break;
      case DataTypeDouble:
	 itemlist->currentItem = *((double*)ReturnOf(itemlist));
	 break;
      case DataTypeFloat:
	 itemlist->currentItem = *((float*)ReturnOf(itemlist));
	 break;
      case DataTypeString:
	 {
	    int i;

	    for (i=0; i < itemlist->listSize; ++i)
	       if (!cmpStrChstr((char*)ReturnOf(itemlist),itemlist->item[i]))
	       {
		  itemlist->currentItem = i;
		  break;
	       }
	    itemlist->currentItem = itemlist->defaultItem;
	    break;
	 }
      }
      drawCDKItemlistField(itemlist, FALSE);
   }
}

static void _saveDataCDKItemlist(CDKOBJS *object)
{
   CDKITEMLIST *itemlist = (CDKITEMLIST *)object;

   if (ReturnOf(itemlist))
   {
      switch (DataTypeOf(itemlist))
      {
      default:
      case DataTypeInt:
	 *((int*)ReturnOf(itemlist)) = itemlist->currentItem;
	 break;
      case DataTypeFloat:
	 *((float*)ReturnOf(itemlist)) = itemlist->currentItem;
	 break;
      case DataTypeDouble:
	 *((double*)ReturnOf(itemlist)) = itemlist->currentItem;
	 break;
      case DataTypeString:
	 chstrncpy((char*)ReturnOf(itemlist), itemlist->item[itemlist->currentItem], 9999);
	 break;
      }
   }
}
#else
dummyRefreshData(Itemlist)
dummySaveData(Itemlist)
#endif

static int createList (CDKITEMLIST *itemlist, char **item, int count)
{
   int status = 0;

   if (count > 0)
   {
      chtype **newItems = typeCallocN(chtype*, count + 1);
      int *newPos = typeCallocN(int, count + 1);
      int *newLen = typeCallocN(int, count + 1);
      int x;
      int fieldWidth = 0;

      if (newItems != 0
	 && newPos != 0
	 && newLen != 0)
      {
	 /* Go through the list and determine the widest item. */
	 status = 1;
	 for (x=0; x < count; x++)
	 {
	    /* Copy the item to the list. */
	    newItems[x] = char2Chtype (item[x], &newLen[x], &newPos[x]);
	    if (newItems[x] == 0)
	    {
	       status = 0;
	       break;
	    }
	    fieldWidth = MAXIMUM (fieldWidth, newLen[x]);
	 }

	 /* Now we need to justify the strings. */
	 for (x=0; x < count; x++)
	 {
	    newPos[x] = justifyString (fieldWidth + 1, newLen[x], newPos[x]);
	 }
      }

      if (status)
      {
	 /* Free up the old memory. */
	 CDKfreeChtypes (itemlist->item);
	 freeChecked (itemlist->itemPos);
	 freeChecked (itemlist->itemLen);

	 /* Copy in the new information. */
	 itemlist->listSize = count;
	 itemlist->item = newItems;
	 itemlist->itemPos = newPos;
	 itemlist->itemLen = newLen;
      }
      else
      {
	 CDKfreeChtypes (newItems);
	 freeChecked (newPos);
	 freeChecked (newLen);
      }
   }
   return status;
}


syntax highlighted by Code2HTML, v. 0.9.1