#include <cdk_int.h>

/*
 * $Author: Thorsten.Glaser $
 * $Date: 2005/04/24 20:27:19 $
 * $Revision: 1.138 $
 */

/*
 * Declare file local prototypes.
 */
static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box);
static int createCDKScrollItemList (CDKSCROLL *scrollp, boolean numbers,
				    char **list, int listSize);
static void setViewSize(CDKSCROLL *scrollp, int listSize);
static int maxViewSize(CDKSCROLL *scrollp);

#define NUMBER_FMT      "%4d. %s"
#define NUMBER_LEN(s)   (8 + strlen(s))

/* Determine how many characters we can shift to the right */
/* before all the items have been scrolled off the screen. */
#define AvailableWidth(w)  ((w)->boxWidth - 2*BorderOf(w))
#define updateViewWidth(w, widest) \
	(w)->maxLeftChar = (((w)->boxWidth > widest) \
			      ? 0 \
			      : (widest - AvailableWidth(w)))
#define WidestItem(w)      ((w)->maxLeftChar + AvailableWidth(w))

DeclareCDKObjects(SCROLL, Scroll, setCdk, Int);

/*
 * This function creates a new scrolling list widget.
 */
CDKSCROLL *newCDKScroll (CDKSCREEN *cdkscreen,
			 int xplace,
			 int yplace,
			 int splace,
			 int height,
			 int width,
			 char *title,
			 char **list,
			 int listSize,
			 boolean numbers,
			 chtype highlight,
			 boolean Box,
			 boolean shadow)
{
   CDKSCROLL *scrollp		= 0;
   int parentWidth		= getmaxx(cdkscreen->window);
   int parentHeight		= getmaxy(cdkscreen->window);
   int boxWidth			= width;
   int boxHeight		= height;
   int xpos			= xplace;
   int ypos			= yplace;
   int scrollAdjust		= 0;
   int x;

   static const struct { int from; int to; } bindings[] = {
		{ CDK_BACKCHAR,	KEY_PPAGE },
		{ CDK_FORCHAR,	KEY_NPAGE },
		{ 'g',		KEY_HOME },
		{ '1',		KEY_HOME },
		{ 'G',		KEY_END },
		{ '<',		KEY_HOME },
		{ '>',		KEY_END },
   };

   if (listSize <= 0
    || (scrollp = newCDKObject(CDKSCROLL, &my_funcs)) == 0)
   {
      destroyCDKObject(scrollp);
      return (0);
   }

   setCDKScrollBox (scrollp, Box);

   /*
    * 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, 0);

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

   /* Set the box height. */
   if (TitleLinesOf(scrollp) > boxHeight)
   {
      boxHeight = TitleLinesOf(scrollp)
      		+ MINIMUM(listSize, 8)
		+ 2 * BorderOf(scrollp);
   }

   /* Adjust the box width if there is a scrollp bar. */
   if ((splace == LEFT) || (splace == RIGHT))
   {
      scrollp->scrollbar = TRUE;
      boxWidth += 1;
   }
   else
   {
      scrollp->scrollbar = FALSE;
   }

  /*
   * Make sure we didn't extend beyond the dimensions of the window.
   */
   scrollp->boxWidth	= (boxWidth > parentWidth ? (parentWidth - scrollAdjust) : boxWidth);
   scrollp->boxHeight	= (boxHeight > parentHeight ? parentHeight : boxHeight);

   setViewSize(scrollp, listSize);

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

   /* Make the scrolling window */
   scrollp->win = newwin (scrollp->boxHeight, scrollp->boxWidth, ypos, xpos);

   /* Is the scrolling window null?? */
   if (scrollp->win == 0)
   {
      destroyCDKObject(scrollp);
      return (0);
   }

   /* Turn the keypad on for the window. */
   keypad (scrollp->win, TRUE);

   /* Create the scrollbar window. */
   if (splace == RIGHT)
   {
      scrollp->scrollbarWin = subwin (scrollp->win,
					maxViewSize(scrollp), 1,
					SCREEN_YPOS(scrollp, ypos),
					xpos + scrollp->boxWidth - BorderOf(scrollp) - 1);
   }
   else if (splace == LEFT)
   {
      scrollp->scrollbarWin = subwin (scrollp->win,
					maxViewSize(scrollp), 1,
					SCREEN_YPOS(scrollp, ypos),
					SCREEN_XPOS(scrollp, xpos));
   }
   else
   {
      scrollp->scrollbarWin = 0;
   }

   /* create the list window */

   scrollp->listWin = subwin(scrollp->win,
			     maxViewSize(scrollp),
			     scrollp->boxWidth - 2*BorderOf(scrollp) - scrollAdjust,
			     SCREEN_YPOS(scrollp, ypos),
			     SCREEN_XPOS(scrollp, xpos) + (splace == LEFT ? 1 : 0));

   /* Set the rest of the variables */
   ScreenOf(scrollp)		= cdkscreen;
   scrollp->parent		= cdkscreen->window;
   scrollp->shadowWin		= 0;
   scrollp->scrollbarPlacement	= splace;
   scrollp->maxLeftChar		= 0;
   scrollp->leftChar		= 0;
   scrollp->highlight		= highlight;
   initExitType(scrollp);
   ObjOf(scrollp)->acceptsFocus = TRUE;
   ObjOf(scrollp)->inputWindow  = scrollp->win;
   scrollp->shadow		= shadow;

   setCDKScrollPosition(scrollp, 0);

   /* Create the scrolling list item list and needed variables. */
   if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0)
   {
      destroyCDKObject(scrollp);
      return (0);
   }

   /* Do we need to create a shadow? */
   if (shadow)
   {
      scrollp->shadowWin = newwin (scrollp->boxHeight, boxWidth, ypos + 1, xpos + 1);
   }

   /* Setup the key bindings. */
   for (x = 0; x < (int) SIZEOF(bindings); ++x)
      bindCDKObject (vSCROLL, scrollp, bindings[x].from, getcCDKBind, (void *)(long)bindings[x].to);

   /* Register this baby. */
   registerCDKObject (cdkscreen, vSCROLL, scrollp);

   /* Return the scrolling list */
   return scrollp;
}

/*
 * This actually does all the 'real' work of managing the scrolling list.
 */
int activateCDKScroll (CDKSCROLL *scrollp, chtype *actions)
{
   /* Draw the scrolling list */
   drawCDKScroll (scrollp, ObjOf(scrollp)->box);

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

      for (;;)
      {
	 /* Get the input. */
	 input = getcCDKObject (ObjOf(scrollp));

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

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

   /* Set the exit type for the widget and return. */
   setExitType(scrollp, 0);
   return -1;
}

/*
 * This injects a single character into the widget.
 */
static int _injectCDKScroll (CDKOBJS *object, chtype input)
{
   CDKSCROLL *scrollp = (CDKSCROLL *)object;
   int ppReturn = 1;
   int ret = unknownInt;
   bool complete = FALSE;

   /* Set the exit type for the widget. */
   setExitType(scrollp, 0);

   /* Draw the scrolling list */
   drawCDKScrollList (scrollp, ObjOf(scrollp)->box);

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

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check for a predefined key binding. */
      if (checkCDKObjectBind (vSCROLL, scrollp, input) != 0)
      {
	 checkEarlyExit(scrollp);
	 complete = TRUE;
      }
      else
      {
	 switch (input)
	 {
	    case KEY_UP :
		 scroller_KEY_UP(scrollp);
		 break;

	    case KEY_DOWN :
		 scroller_KEY_DOWN(scrollp);
		 break;

	    case KEY_RIGHT :
		 scroller_KEY_RIGHT(scrollp);
		 break;

	    case KEY_LEFT :
		 scroller_KEY_LEFT(scrollp);
		 break;

	    case KEY_PPAGE :
		 scroller_KEY_PPAGE(scrollp);
		 break;

	    case KEY_NPAGE :
		 scroller_KEY_NPAGE(scrollp);
		 break;

	    case KEY_HOME :
		 scroller_KEY_HOME(scrollp);
		 break;

	    case KEY_END :
		 scroller_KEY_END(scrollp);
		 break;

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

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

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

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

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

	    default :
		 break;
	 }
      }

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

   if (!complete) {
      drawCDKScrollList (scrollp, ObjOf(scrollp)->box);
      setExitType(scrollp, 0);
   }

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

/*
 * This allows the user to accelerate to a position in the scrolling list.
 */
void setCDKScrollPosition (CDKSCROLL *scrollp, int item)
{
   scroller_SetPosition(scrollp, item);
}

int getCDKScrollCurrent (CDKSCROLL *scrollp)
{
   return scrollp->currentItem;
}

void setCDKScrollCurrent (CDKSCROLL *scrollp, int item)
{
   scroller_SetPosition(scrollp, item);
}

/*
 * This moves the scroll field to the given location.
 */
static void _moveCDKScroll (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
   CDKSCROLL *scrollp = (CDKSCROLL *)object;
   int currentX = getbegx(scrollp->win);
   int currentY = getbegy(scrollp->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(scrollp->win) + xplace;
      ypos = getbegy(scrollp->win) + yplace;
   }

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

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

   /* Move the window to the new location. */
   moveCursesWindow(scrollp->win, -xdiff, -ydiff);
   moveCursesWindow(scrollp->listWin, -xdiff, -ydiff);
   moveCursesWindow(scrollp->shadowWin, -xdiff, -ydiff);
   moveCursesWindow(scrollp->scrollbarWin, -xdiff, -ydiff);

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

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

/*
 * This function draws the scrolling list widget.
 */
static void _drawCDKScroll (CDKOBJS *object, boolean Box)
{
   CDKSCROLL *scrollp = (CDKSCROLL *)object;

   /* Draw in the shadow if we need to. */
   if (scrollp->shadowWin != 0)
      drawShadow (scrollp->shadowWin);

   drawCdkTitle (scrollp->win, object);

   /* Draw in the scolling list items. */
   drawCDKScrollList (scrollp, Box);
}

static void drawCDKScrollCurrent(CDKSCROLL *s)
{
   /* Rehighlight the current menu item. */
   int screenPos = s->itemPos[s->currentItem] - s->leftChar;
   chtype highlight = HasFocusObj(s) ? s->highlight : A_NORMAL;

   writeChtypeAttrib (s->listWin,
		      (screenPos >= 0) ? screenPos : 0,
		      s->currentHigh,
		      s->item[s->currentItem],
		      highlight,
		      HORIZONTAL,
		      (screenPos >= 0) ? 0 : (1 - screenPos),
		      s->itemLen[s->currentItem]);
}

static int maxViewSize(CDKSCROLL *scrollp)
{
   return scroller_MaxViewSize(scrollp);
}

/*
 * Set variables that depend upon the list-size.
 */
static void setViewSize(CDKSCROLL *scrollp, int listSize)
{
   scroller_SetViewSize(scrollp, listSize);
}

#define SCREENPOS(w,n) (w)->itemPos[n] - (w)->leftChar /* + scrollbarAdj + BorderOf(w) */
#undef  SCREEN_YPOS		/* because listWin is separate */
#define SCREEN_YPOS(w,n) (n)

/*
 * This redraws the scrolling list.
 */
static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box)
{
   int j;

   /* If the list is empty, don't draw anything. */
   if (scrollp->listSize > 0)
   {
      /* Redraw the list */
      for (j=0; j < scrollp->viewSize; j++)
      {
	 int k = j + scrollp->currentTop;

	 writeBlanks (scrollp->listWin, 0, j, HORIZONTAL, 0, scrollp->boxWidth - 2 * BorderOf(scrollp));

	 /* Draw the elements in the scrolling list. */
	 if (k < scrollp->listSize)
	 {
	    int screenPos = SCREENPOS(scrollp, j + scrollp->currentTop);
	    int ypos = SCREEN_YPOS(scrollp, j);

	    /* Write in the correct line. */
	    writeChtype (scrollp->listWin,
			 (screenPos >= 0) ? screenPos : 1,
			 ypos,
			 scrollp->item[k],
			 HORIZONTAL,
			 (screenPos >= 0) ? 0 : (1 - screenPos),
			 scrollp->itemLen[k]);
	 }
      }

      drawCDKScrollCurrent(scrollp);

      /* Determine where the toggle is supposed to be. */
      if (scrollp->scrollbarWin != 0)
      {
	 scrollp->togglePos = floorCDK(scrollp->currentItem * scrollp->step);

	 /* Make sure the toggle button doesn't go out of bounds. */

	 if (scrollp->togglePos >= getmaxy(scrollp->scrollbarWin))
	    scrollp->togglePos = getmaxy(scrollp->scrollbarWin) - 1;

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

   /* Box it if needed. */
   if (Box) {
      drawObjBox (scrollp->win, ObjOf(scrollp));
   }

   /* Refresh the window. */
   refreshCDKWindow (scrollp->win);
}

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

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

/*
 * This function destroys
 */
static void _destroyCDKScroll (CDKOBJS *object)
{
   if (object != 0)
   {
      CDKSCROLL *scrollp = (CDKSCROLL *)object;

      cleanCdkTitle (object);
      CDKfreeChtypes (scrollp->item);
      freeChecked (scrollp->itemPos);
      freeChecked (scrollp->itemLen);

      /* Clean up the windows. */
      deleteCursesWindow (scrollp->scrollbarWin);
      deleteCursesWindow (scrollp->shadowWin);
      deleteCursesWindow (scrollp->listWin);
      deleteCursesWindow (scrollp->win);

      /* Unregister this object. */
      unregisterCDKObject (vSCROLL, scrollp);
   }
}

/*
 * This function erases the scrolling list from the screen.
 */
static void _eraseCDKScroll (CDKOBJS *object)
{
   if (validCDKObject (object))
   {
      CDKSCROLL *scrollp = (CDKSCROLL *)object;

      eraseCursesWindow (scrollp->win);
      eraseCursesWindow (scrollp->shadowWin);
   }
}

static boolean allocListArrays(CDKSCROLL *scrollp,
			       int oldSize,
			       int newSize)
{
   boolean result;
   int n;
   int nchunk = ((newSize + 1) | 31) + 1;
   chtype ** newList	= typeCallocN(chtype *, nchunk);
   int * newLen		= typeCallocN(int, nchunk);
   int * newPos		= typeCallocN(int, nchunk);

   if (newList != 0
    && newLen != 0
    && newPos != 0)
   {
      for (n = 0; n < oldSize; ++n)
      {
	 newList[n] = scrollp->item[n];
	 newLen[n]  = scrollp->itemLen[n];
	 newPos[n]  = scrollp->itemPos[n];
      }

      if (oldSize == 0)
      {
	 CDKfreeChtypes (scrollp->item);
	 freeChecked (scrollp->itemPos);
	 freeChecked (scrollp->itemLen);
      }

      scrollp->item    = newList;
      scrollp->itemLen = newLen;
      scrollp->itemPos = newPos;
      result = TRUE;
   }
   else
   {
      freeChecked(newList);
      freeChecked(newLen);
      freeChecked(newPos);
      result = FALSE;
   }
   return result;
}

static boolean allocListItem(CDKSCROLL *scrollp,
			     int which,
			     char **work,
			     unsigned *used,
			     int number,
			     char *value)
{
   if (number > 0)
   {
      unsigned need = NUMBER_LEN(value);
      if (need > *used)
      {
	 *used = ((need + 2) * 2);
	 if (*work == 0)
	 {
	    if ((*work = (char *)malloc(*used)) == 0)
	       return FALSE;
	 }
	 else
	 {
	    if ((*work = (char *)realloc(*work, *used)) == 0)
	       return FALSE;
	 }
      }
      sprintf (*work, NUMBER_FMT, number, value);
      value = *work;
   }

   if ((scrollp->item[which] = char2Chtype (value,
					    &(scrollp->itemLen[which]),
					    &(scrollp->itemPos[which]))) == 0)
      return FALSE;

   scrollp->itemPos[which]  = justifyString (scrollp->boxWidth,
					     scrollp->itemLen[which],
					     scrollp->itemPos[which]);

   return TRUE;
}

/*
 * This function creates the scrolling list information and sets up the needed
 * variables for the scrolling list to work correctly.
 */
static int createCDKScrollItemList (CDKSCROLL *scrollp, boolean numbers, char **list, int listSize)
{
   int status = 0;

   if (listSize > 0)
   {
      int widestItem		= 0;
      int x			= 0;
      unsigned have		= 0;
      char *temp		= 0;

      if (allocListArrays(scrollp, 0, listSize))
      {
	 /* Create the items in the scrolling list. */
	 status = 1;
	 for (x=0 ; x < listSize; x++)
	 {
	    if (!allocListItem(scrollp,
			       x,
			       &temp,
			       &have,
			       numbers ? (x + 1) : 0,
			       list[x]))
	    {
	       status = 0;
	       break;
	    }

	    widestItem = MAXIMUM (scrollp->itemLen[x], widestItem);
	 }
	 freeChecked (temp);

	 if (status)
	 {
	    updateViewWidth(scrollp, widestItem);

	    /* Keep the boolean flag 'numbers' */
	    scrollp->numbers = numbers;
	 }
      }
   }
   return status;
}

/*
 * This sets certain attributes of the scrolling list.
 */
void setCDKScroll (CDKSCROLL *scrollp, char **list, int listSize, boolean numbers, chtype highlight, boolean Box)
{
   setCDKScrollItems (scrollp, list, listSize, numbers);
   setCDKScrollHighlight (scrollp, highlight);
   setCDKScrollBox (scrollp, Box);
}

/*
 * This sets the scrolling list items.
 */
void setCDKScrollItems (CDKSCROLL *scrollp, char **list, int listSize, boolean numbers)
{
   int x = 0;

   if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0)
      return;

   /* Clean up the display. */
   for (x=0; x < scrollp->viewSize ; x++)
   {
      writeBlanks (scrollp->win, 1, SCREEN_YPOS(scrollp, x),
		   HORIZONTAL, 0, scrollp->boxWidth-2);
   }

   setViewSize(scrollp, listSize);
   setCDKScrollPosition(scrollp, 0);
   scrollp->leftChar	= 0;
}
int getCDKScrollItems (CDKSCROLL *scrollp, char *list[])
{
   int x;

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

/*
 * This sets the highlight of the scrolling list.
 */
void setCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight)
{
   scrollp->highlight = highlight;
}
chtype getCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight GCC_UNUSED)
{
   return scrollp->highlight;
}

/*
 * This sets the box attribute of the scrolling list.
 */
void setCDKScrollBox (CDKSCROLL *scrollp, boolean Box)
{
   ObjOf(scrollp)->box = Box;
   ObjOf(scrollp)->borderSize = Box ? 1 : 0;
}
boolean getCDKScrollBox (CDKSCROLL *scrollp)
{
   return ObjOf(scrollp)->box;
}

/*
 * Resequence the numbers after a insertion/deletion.
 */
static void resequence (CDKSCROLL *scrollp)
{
   if (scrollp->numbers)
   {
      int j, k;
      for (j = 0; j < scrollp->listSize; ++j)
      {
	 char source[80];
	 chtype *target = scrollp->item[j];

	 sprintf(source, NUMBER_FMT, j + 1, "");

	 for (k = 0; source[k] != 0; ++k)
	 {
	    /* handle deletions that change the length of number */
	    if (source[k] == '.' && CharOf(target[k]) != '.')
	    {
	       int k2 = k;
	       while ((target[k2] = target[k2 + 1]) != 0)
		  ++k2;
	       scrollp->itemLen[j] -= 1;
	    }
	    target[k] &= A_ATTRIBUTES;
	    target[k] |= source[k];
	 }
      }
   }
}

static boolean insertListItem (CDKSCROLL *scrollp, int item)
{
   int x;
   for (x = scrollp->listSize; x > item; --x)
   {
      scrollp->item[x]		= scrollp->item[x - 1];
      scrollp->itemLen[x]	= scrollp->itemLen[x - 1];
      scrollp->itemPos[x]	= scrollp->itemPos[x - 1];
   }
   return TRUE;
}

/*
 * This adds a single item to a scrolling list, at the end of the list.
 */
void addCDKScrollItem (CDKSCROLL *scrollp, char *item)
{
   int itemNumber = scrollp->listSize;
   int widestItem = WidestItem(scrollp);
   char *temp = 0;
   unsigned have = 0;

   if (allocListArrays(scrollp, scrollp->listSize, scrollp->listSize + 1)
    && allocListItem(scrollp,
		     itemNumber,
		     &temp,
		     &have,
		     scrollp->numbers ? (itemNumber + 1) : 0,
		     item))
   {
      /* Determine the size of the widest item. */
      widestItem = MAXIMUM (scrollp->itemLen[itemNumber], widestItem);

      updateViewWidth(scrollp, widestItem);

      setViewSize(scrollp, scrollp->listSize + 1);
   }

   freeChecked(temp);
}

/*
 * This adds a single item to a scrolling list, before the current item.
 */
void insertCDKScrollItem (CDKSCROLL *scrollp, char *item)
{
   int widestItem = WidestItem(scrollp);
   char *temp = 0;
   unsigned have = 0;

   if (allocListArrays(scrollp, scrollp->listSize, scrollp->listSize + 1)
    && insertListItem(scrollp, scrollp->currentItem)
    && allocListItem(scrollp,
		     scrollp->currentItem,
		     &temp,
		     &have,
		     scrollp->numbers ? (scrollp->currentItem + 1) : 0,
		     item))
   {
      /* Determine the size of the widest item. */
      widestItem = MAXIMUM (scrollp->itemLen[scrollp->currentItem], widestItem);

      updateViewWidth(scrollp, widestItem);

      setViewSize(scrollp, scrollp->listSize + 1);

      resequence(scrollp);
   }

   freeChecked(temp);
}

/*
 * This removes a single item from a scrolling list.
 */
void deleteCDKScrollItem (CDKSCROLL *scrollp, int position)
{
   int x;

   if (position >= 0 && position < scrollp->listSize)
   {
      freeChtype (scrollp->item[position]);

      /* Adjust the list. */
      for (x=position; x < scrollp->listSize; x++)
      {
	 scrollp->item[x]	= scrollp->item[x + 1];
	 scrollp->itemLen[x]	= scrollp->itemLen[x + 1];
	 scrollp->itemPos[x]	= scrollp->itemPos[x + 1];
      }
      setViewSize(scrollp, scrollp->listSize - 1);

      if (scrollp->listSize > 0)
	 resequence(scrollp);

      if (scrollp->listSize < maxViewSize(scrollp))
	 werase(scrollp->win);	/* force the next redraw to be complete */

      /* do this to update the view size, etc. */
      setCDKScrollPosition (scrollp, scrollp->currentItem);
   }
}

static void _focusCDKScroll(CDKOBJS *object)
{
   CDKSCROLL *scrollp = (CDKSCROLL *)object;

   drawCDKScrollCurrent(scrollp);
   wrefresh (scrollp->listWin);
}

static void _unfocusCDKScroll(CDKOBJS *object)
{
   CDKSCROLL *scrollp = (CDKSCROLL *)object;

   drawCDKScrollCurrent(scrollp);
   wrefresh (scrollp->listWin);
}

dummyRefreshData(Scroll)

dummySaveData(Scroll)


syntax highlighted by Code2HTML, v. 0.9.1