#include <cdk_int.h>

/*
 * $Author: tom $
 * $Date: 2016/11/20 20:13:16 $
 * $Revision: 1.142 $
 */

/*
 * Declare file local prototypes.
 */
static void CDKTemplateCallBack (CDKTEMPLATE *widget, chtype input);
static void drawCDKTemplateField (CDKTEMPLATE *widget);
static void adjustCDKTemplateCursor (CDKTEMPLATE *widget, int direction);

#define isPlateChar(c) ((c) != 0 && strchr ("#ACcMXz", c) != 0)

DeclareCDKObjects (TEMPLATE, Template, setCdk, String);

/*
 * This creates a cdktemplate widget.
 */
CDKTEMPLATE *newCDKTemplate (CDKSCREEN *cdkscreen,
			     int xplace,
			     int yplace,
			     const char *title,
			     const char *label,
			     const char *plate,
			     const char *Overlay,
			     boolean Box,
			     boolean shadow)
{
   /* *INDENT-EQLS* */
   CDKTEMPLATE *cdktemplate     = 0;
   int parentWidth              = getmaxx (cdkscreen->window);
   int parentHeight             = getmaxy (cdkscreen->window);
   int boxWidth                 = 0;
   int boxHeight                = Box ? 3 : 1;
   int xpos                     = xplace;
   int ypos                     = yplace;
   int horizontalAdjust, oldWidth;
   int fieldWidth               = 0;
   int plateLen                 = 0;
   int junk                     = 0;

   if (plate == 0
       || (cdktemplate = newCDKObject (CDKTEMPLATE, &my_funcs)) == 0)
        return (0);

   setCDKTemplateBox (cdktemplate, Box);

   fieldWidth = (int)strlen (plate) + 2 * BorderOf (cdktemplate);

   /* *INDENT-EQLS* Set some basic values of the cdktemplate field. */
   cdktemplate->label     = 0;
   cdktemplate->labelLen  = 0;
   cdktemplate->labelWin  = 0;

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

   /* Translate the char * Overlay to a chtype * */
   if (Overlay != 0)
   {
      cdktemplate->overlay = char2Chtype (Overlay, &cdktemplate->overlayLen, &junk);
      cdktemplate->fieldAttr = cdktemplate->overlay[0] & A_ATTRIBUTES;
   }
   else
   {
      /* *INDENT-EQLS* */
      cdktemplate->overlay      = 0;
      cdktemplate->overlayLen   = 0;
      cdktemplate->fieldAttr    = A_NORMAL;
   }

   /* Set the box width. */
   boxWidth = fieldWidth + cdktemplate->labelLen + 2 * BorderOf (cdktemplate);

   oldWidth = boxWidth;
   boxWidth = setCdkTitle (ObjOf (cdktemplate), title, boxWidth);
   horizontalAdjust = (boxWidth - oldWidth) / 2;

   boxHeight += TitleLinesOf (cdktemplate);

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

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

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

   /* Is the cdktemplate window null?? */
   if (cdktemplate->win == 0)
   {
      destroyCDKObject (cdktemplate);
      return (0);
   }
   keypad (cdktemplate->win, TRUE);

   /* Make the label window. */
   if (cdktemplate->label != 0)
   {
      cdktemplate->labelWin = subwin (cdktemplate->win,
				      1, cdktemplate->labelLen,
				      (ypos +
				       TitleLinesOf (cdktemplate) +
				       BorderOf (cdktemplate)),
				      (xpos +
				       horizontalAdjust +
				       BorderOf (cdktemplate)));
   }

   /* Make the field window. */
   cdktemplate->fieldWin = subwin (cdktemplate->win,
				   1, fieldWidth,
				   (ypos +
				    TitleLinesOf (cdktemplate) +
				    BorderOf (cdktemplate)),
				   (xpos +
				    cdktemplate->labelLen +
				    horizontalAdjust +
				    BorderOf (cdktemplate)));
   keypad (cdktemplate->fieldWin, TRUE);

   /* Set up the info field. */
   cdktemplate->plateLen = (int)strlen (plate);
   cdktemplate->info = typeCallocN (char, cdktemplate->plateLen + 2);
   if (cdktemplate->info == 0)
   {
      destroyCDKObject (cdktemplate);
      return (0);
   }

   /* Copy the plate to the cdktemplate. */
   plateLen = (int)strlen (plate);
   cdktemplate->plate = typeMallocN (char, plateLen + 3);
   if (cdktemplate->plate == 0)
   {
      destroyCDKObject (cdktemplate);
      return (0);
   }
   strcpy (cdktemplate->plate, plate);

   /* *INDENT-EQLS* Set up the rest of the structure */
   ScreenOf (cdktemplate)               = cdkscreen;
   cdktemplate->parent                  = cdkscreen->window;
   cdktemplate->shadowWin               = 0;
   cdktemplate->fieldWidth              = fieldWidth;
   cdktemplate->boxHeight               = boxHeight;
   cdktemplate->boxWidth                = boxWidth;
   cdktemplate->platePos                = 0;
   cdktemplate->screenPos               = 0;
   cdktemplate->infoPos                 = 0;
   initExitType (cdktemplate);
   cdktemplate->min                     = 0;
   ObjOf (cdktemplate)->inputWindow     = cdktemplate->win;
   ObjOf (cdktemplate)->acceptsFocus    = TRUE;
   cdktemplate->shadow                  = shadow;
   cdktemplate->callbackfn              = CDKTemplateCallBack;

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

   registerCDKObject (cdkscreen, vTEMPLATE, cdktemplate);

   return cdktemplate;
}

/*
 * This actually manages the cdktemplate widget...
 */
char *activateCDKTemplate (CDKTEMPLATE *cdktemplate, chtype *actions)
{
   chtype input = 0;
   boolean functionKey;
   char *ret = 0;

   /* Draw the object. */
   drawCDKTemplate (cdktemplate, ObjOf (cdktemplate)->box);

   if (actions == 0)
   {
      for (;;)
      {
	 input = (chtype)getchCDKObject (ObjOf (cdktemplate), &functionKey);

	 /* Inject the character into the widget. */
	 ret = injectCDKTemplate (cdktemplate, input);
	 if (cdktemplate->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 = injectCDKTemplate (cdktemplate, actions[x]);
	 if (cdktemplate->exitType != vEARLY_EXIT)
	 {
	    return ret;
	 }
      }
   }

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

/*
 * This injects a character into the widget.
 */
static int _injectCDKTemplate (CDKOBJS *object, chtype input)
{
   CDKTEMPLATE *widget = (CDKTEMPLATE *)object;
   int ppReturn = 1;
   char *ret = unknownString;
   bool complete = FALSE;

   /* Set the exit type and return. */
   setExitType (widget, 0);

   /* Move the cursor. */
   drawCDKTemplateField (widget);

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

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check a predefined binding...  */
      if (checkCDKObjectBind (vTEMPLATE, widget, input) != 0)
      {
	 checkEarlyExit (widget);
	 complete = TRUE;
      }
      else
      {
	 switch (input)
	 {
	 case CDK_ERASE:
	    if (strlen (widget->info) != 0)
	    {
	       cleanCDKTemplate (widget);
	       drawCDKTemplateField (widget);
	    }
	    break;

	 case CDK_CUT:
	    if ((int)strlen (widget->info) != 0)
	    {
	       freeChar (GPasteBuffer);
	       GPasteBuffer = copyChar (widget->info);
	       cleanCDKTemplate (widget);
	       drawCDKTemplateField (widget);
	    }
	    else
	    {
	       Beep ();
	    }
	    break;

	 case CDK_COPY:
	    if ((int)strlen (widget->info) != 0)
	    {
	       freeChar (GPasteBuffer);
	       GPasteBuffer = copyChar (widget->info);
	    }
	    else
	    {
	       Beep ();
	    }
	    break;

	 case CDK_PASTE:
	    if (GPasteBuffer != 0)
	    {
	       int length, x;

	       cleanCDKTemplate (widget);

	       /* Start inserting each character one at a time. */
	       length = (int)strlen (GPasteBuffer);
	       for (x = 0; x < length; x++)
	       {
		  (widget->callbackfn) (widget, (chtype)GPasteBuffer[x]);
	       }
	       drawCDKTemplateField (widget);
	    }
	    else
	    {
	       Beep ();
	    }
	    break;

	 case KEY_TAB:
	 case KEY_ENTER:
	    if ((int)strlen (widget->info) < (int)widget->min)
	    {
	       Beep ();
	    }
	    else
	    {
	       setExitType (widget, input);
	       ret = widget->info;
	       complete = TRUE;
	    }
	    break;

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

	 case KEY_ERROR:
	    setExitType (widget, input);
	    complete = TRUE;
	    break;

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

	 default:
	    (widget->callbackfn) (widget, input);
	    break;
	 }
      }

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

   if (!complete)
   {
      setExitType (widget, 0);
   }

   ResultOf (widget).valueString = ret;
   return (ret != unknownString);
}

/*
 * Return true if the given string matches the template (may be incomplete).
 */
static boolean validTemplate (CDKTEMPLATE *cdktemplate, char *input)
{
   int pp, ip;
   const char *plate = cdktemplate->plate;

   for (pp = 0, ip = 0; input[ip] != '\0' && plate[pp] != '\0'; ++ip, ++pp)
   {
      int newchar = input[ip];

      while (plate[pp] != '\0' && !isPlateChar (plate[pp]))
      {
	 ++pp;
      }
      if (plate[pp] == 0)
      {
	 return FALSE;
      }

      /* check if the input matches the plate */
      if (isdigit (CharOf (newchar)) &&
	  (plate[pp] == 'A' ||
	   plate[pp] == 'C' ||
	   plate[pp] == 'c'))
      {
	 return FALSE;
      }
      if (!isdigit (CharOf (newchar)) &&
	  plate[pp] == '#')
      {
	 return FALSE;
      }

      /* Do we need to convert the case??? */
      if (plate[pp] == 'C' ||
	  plate[pp] == 'X')
      {
	 newchar = toupper (newchar);
      }
      else if (plate[pp] == 'c' ||
	       plate[pp] == 'x')
      {
	 newchar = tolower (newchar);
      }
      input[ip] = (char)newchar;
   }
   return TRUE;
}

/*
 * This is the standard callback proc for the cdktemplate.
 */
static void CDKTemplateCallBack (CDKTEMPLATE *cdktemplate, chtype input)
{
   boolean failed = FALSE;
   boolean change = FALSE;
   boolean moveby = FALSE;
   int amount = 0;
   size_t mark = (size_t) cdktemplate->infoPos;
   size_t have = strlen (cdktemplate->info);

   if (input == KEY_LEFT)
   {
      if (mark != 0)
      {
	 moveby = TRUE;
	 amount = -1;
      }
      else
      {
	 failed = TRUE;
      }
   }
   else if (input == KEY_RIGHT)
   {
      if (cdktemplate->info[mark] != '\0')
      {
	 moveby = TRUE;
	 amount = 1;
      }
      else
      {
	 failed = TRUE;
      }
   }
   else
   {
      char *test = (char *)malloc (have + 2);

      if (test != 0)
      {
	 strcpy (test, cdktemplate->info);

	 if (input == KEY_BACKSPACE)
	 {
	    if (mark != 0)
	    {
	       strcpy (test + mark - 1, cdktemplate->info + mark);
	       change = TRUE;
	       amount = -1;
	    }
	    else
	    {
	       failed = TRUE;
	    }
	 }
	 else if (input == KEY_DC)
	 {
	    if (test[mark] != '\0')
	    {
	       strcpy (test + mark, cdktemplate->info + mark + 1);
	       change = TRUE;
	       amount = 0;
	    }
	    else
	    {
	       failed = TRUE;
	    }
	 }
	 else if (isChar (input) &&
		  cdktemplate->platePos < cdktemplate->fieldWidth)
	 {
	    test[mark] = (char)(input);
	    strcpy (test + mark + 1, cdktemplate->info + mark + 1);
	    change = TRUE;
	    amount = 1;
	 }
	 else
	 {
	    failed = TRUE;
	 }

	 if (change)
	 {
	    if (validTemplate (cdktemplate, test))
	    {
	       strcpy (cdktemplate->info, test);
	       drawCDKTemplateField (cdktemplate);
	    }
	    else
	    {
	       failed = TRUE;
	    }
	 }

	 free (test);
      }
   }

   if (failed)
   {
      Beep ();
   }
   else if (change || moveby)
   {
      cdktemplate->infoPos += amount;
      cdktemplate->platePos += amount;
      cdktemplate->screenPos += amount;

      adjustCDKTemplateCursor (cdktemplate, amount);
   }
}

/*
 * Return a mixture of the plate-overlay and field-info.
 */
char *mixCDKTemplate (CDKTEMPLATE *cdktemplate)
{
   char *mixedString = 0;

   if (cdktemplate->info != 0 && cdktemplate->info[0] != '\0')
   {
      mixedString = typeCallocN (char, cdktemplate->plateLen + 3);

      if (mixedString != 0)
      {
	 int infoPos = 0;
	 int platePos = 0;

	 while (platePos < cdktemplate->plateLen)
	 {
	    mixedString[platePos] = (char)(isPlateChar (cdktemplate->plate[platePos])
					   ? cdktemplate->info[infoPos++]
					   : cdktemplate->plate[platePos]);
	    platePos++;
	 }
      }
   }

   return mixedString;
}

/*
 * Return the field-info from the mixed string.
 */
char *unmixCDKTemplate (CDKTEMPLATE *cdktemplate, const char *info)
{
   /* *INDENT-EQLS* */
   int infolen          = (int)strlen (info);
   char *unmixedString  = typeCallocN (char, infolen + 2);

   if (unmixedString != 0)
   {
      int pos = 0;
      int x = 0;

      while (pos < infolen)
      {
	 if (isPlateChar (cdktemplate->plate[pos]))
	 {
	    unmixedString[x++] = info[pos++];
	 }
	 else
	 {
	    pos++;
	 }
      }
   }

   return unmixedString;
}

/*
 * Move the cdktemplate field to the given location.
 */
static void _moveCDKTemplate (CDKOBJS *object,
			      int xplace,
			      int yplace,
			      boolean relative,
			      boolean refresh_flag)
{
   CDKTEMPLATE *cdktemplate = (CDKTEMPLATE *)object;
   /* *INDENT-EQLS* */
   int currentX = getbegx (cdktemplate->win);
   int currentY = getbegy (cdktemplate->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 (cdktemplate->win) + xplace;
      ypos = getbegy (cdktemplate->win) + yplace;
   }

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

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

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

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

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

/*
 * Draw the template widget.
 */
static void _drawCDKTemplate (CDKOBJS *object, boolean Box)
{
   CDKTEMPLATE *cdktemplate = (CDKTEMPLATE *)object;

   /* Do we need to draw the shadow. */
   if (cdktemplate->shadowWin != 0)
   {
      drawShadow (cdktemplate->shadowWin);
   }

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

   drawCdkTitle (cdktemplate->win, object);

   wrefresh (cdktemplate->win);

   drawCDKTemplateField (cdktemplate);
}

/*
 * Draw the cdktemplate field.
 */
static void drawCDKTemplateField (CDKTEMPLATE *cdktemplate)
{
   /* *INDENT-EQLS* */
   chtype fieldColor    = 0;
   int infolen          = (int)strlen (cdktemplate->info);

   /* Draw in the label and the cdktemplate object. */
   if (cdktemplate->labelWin != 0)
   {
      writeChtype (cdktemplate->labelWin, 0, 0,
		   cdktemplate->label,
		   HORIZONTAL, 0,
		   cdktemplate->labelLen);
      wrefresh (cdktemplate->labelWin);
   }

   /* Draw in the cdktemplate... */
   if (cdktemplate->overlay != 0)
   {
      writeChtype (cdktemplate->fieldWin, 0, 0,
		   cdktemplate->overlay,
		   HORIZONTAL, 0,
		   cdktemplate->overlayLen);

      /* Adjust the cursor. */
      if (infolen != 0)
      {
	 int pos = 0;
	 int x = 0;
	 for (x = 0; x < cdktemplate->fieldWidth; x++)
	 {
	    if (isPlateChar (cdktemplate->plate[x]) && pos < infolen)
	    {
	       fieldColor = cdktemplate->overlay[x] & A_ATTRIBUTES;
	       (void)mvwaddch (cdktemplate->fieldWin,
			       0, x,
			       CharOf (cdktemplate->info[pos++]) | fieldColor);
	    }
	 }
	 wmove (cdktemplate->fieldWin, 0, cdktemplate->screenPos);
      }
      else
      {
	 adjustCDKTemplateCursor (cdktemplate, +1);
      }
      wrefresh (cdktemplate->fieldWin);
   }
}

/*
 * Adjust the cursor for the cdktemplate.
 */
static void adjustCDKTemplateCursor (CDKTEMPLATE *cdktemplate, int direction)
{
   while (!isPlateChar (cdktemplate->plate[cdktemplate->platePos])
	  && cdktemplate->platePos < cdktemplate->fieldWidth)
   {
      cdktemplate->platePos += direction;
      cdktemplate->screenPos += direction;
   }
   wmove (cdktemplate->fieldWin, 0, cdktemplate->screenPos);
   wrefresh (cdktemplate->fieldWin);
}

/*
 * Set the background attribute of the widget.
 */
static void _setBKattrTemplate (CDKOBJS *object, chtype attrib)
{
   if (object != 0)
   {
      CDKTEMPLATE *widget = (CDKTEMPLATE *)object;

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

/*
 * Destroy this widget.
 */
static void _destroyCDKTemplate (CDKOBJS *object)
{
   if (object != 0)
   {
      CDKTEMPLATE *cdktemplate = (CDKTEMPLATE *)object;

      cleanCdkTitle (object);
      freeChtype (cdktemplate->label);
      freeChtype (cdktemplate->overlay);
      freeChar (cdktemplate->plate);
      freeChar (cdktemplate->info);

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

      /* Clean the key bindings. */
      cleanCDKObjectBindings (vTEMPLATE, cdktemplate);

      unregisterCDKObject (vTEMPLATE, cdktemplate);
   }
}

/*
 * Erase the widget.
 */
static void _eraseCDKTemplate (CDKOBJS *object)
{
   if (validCDKObject (object))
   {
      CDKTEMPLATE *cdktemplate = (CDKTEMPLATE *)object;

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

/*
 * Set the value given to the cdktemplate.
 */
void setCDKTemplate (CDKTEMPLATE *cdktemplate, const char *newValue, boolean Box)
{
   setCDKTemplateValue (cdktemplate, newValue);
   setCDKTemplateBox (cdktemplate, Box);
}

/*
 * Set the value given to the cdktemplate.
 */
void setCDKTemplateValue (CDKTEMPLATE *cdktemplate, const char *newValue)
{
   /* *INDENT-EQLS* */
   int len              = 0;
   int copychars        = 0;
   int x;

   cleanCDKTemplate (cdktemplate);

   /* Just to be sure, if let's make sure the new value isn't null. */
   if (newValue == 0)
   {
      return;
   }

   /* Determine how many characters we need to copy. */
   len = (int)strlen (newValue);
   copychars = MINIMUM (len, cdktemplate->fieldWidth);

   /* OK, erase the old value, and copy in the new value. */
   cdktemplate->info[0] = '\0';
   strncpy (cdktemplate->info, newValue, (size_t) copychars);

   /* Use the function which handles the input of the characters. */
   for (x = 0; x < len; x++)
   {
      (cdktemplate->callbackfn) (cdktemplate, (chtype)newValue[x]);
   }
}
char *getCDKTemplateValue (CDKTEMPLATE *cdktemplate)
{
   return cdktemplate->info;
}

/*
 * Set the minimum number of characters to enter into the widget.
 */
void setCDKTemplateMin (CDKTEMPLATE *cdktemplate, int min)
{
   if (min >= 0)
   {
      cdktemplate->min = min;
   }
}
int getCDKTemplateMin (CDKTEMPLATE *cdktemplate)
{
   return cdktemplate->min;
}

/*
 * Set the box attribute of the cdktemplate widget.
 */
void setCDKTemplateBox (CDKTEMPLATE *cdktemplate, boolean Box)
{
   ObjOf (cdktemplate)->box = Box;
   ObjOf (cdktemplate)->borderSize = Box ? 1 : 0;
}
boolean getCDKTemplateBox (CDKTEMPLATE *cdktemplate)
{
   return ObjOf (cdktemplate)->box;
}

/*
 * Erase the information in the cdktemplate widget.
 */
void cleanCDKTemplate (CDKTEMPLATE *cdktemplate)
{
   if (cdktemplate->fieldWidth > 0)
      memset (cdktemplate->info, 0, (size_t) cdktemplate->fieldWidth);

   /* *INDENT-EQLS* */
   cdktemplate->screenPos = 0;
   cdktemplate->infoPos   = 0;
   cdktemplate->platePos  = 0;
}

/*
 * Set the callback function for the widget.
 */
void setCDKTemplateCB (CDKTEMPLATE *cdktemplate, TEMPLATECB callback)
{
   cdktemplate->callbackfn = callback;
}

static void _focusCDKTemplate (CDKOBJS *object)
{
   CDKTEMPLATE *widget = (CDKTEMPLATE *)object;

   drawCDKTemplate (widget, ObjOf (widget)->box);
}

static void _unfocusCDKTemplate (CDKOBJS *object)
{
   CDKTEMPLATE *widget = (CDKTEMPLATE *)object;

   drawCDKTemplate (widget, ObjOf (widget)->box);
}

dummyRefreshData (Template)

dummySaveData (Template)