#include <cdk_int.h>

/*
 * $Author: tom $
 * $Date: 2016/11/20 19:04:57 $
 * $Revision: 1.224 $
 */

/*
 * Declare file local prototypes.
 */
static void CDKEntryCallBack (CDKENTRY *entry, chtype character);
static void drawCDKEntryField (CDKENTRY *entry);

DeclareCDKObjects (ENTRY, Entry, setCdk, String);

/*
 * This creates a pointer to an entry widget.
 */
CDKENTRY *newCDKEntry (CDKSCREEN *cdkscreen,
		       int xplace,
		       int yplace,
		       const char *title,
		       const char *label,
		       chtype fieldAttr,
		       chtype filler,
		       EDisplayType dispType,
		       int fWidth,
		       int min,
		       int max,
		       boolean Box,
		       boolean shadow)
{
   /* *INDENT-EQLS* */
   CDKENTRY *entry      = 0;
   int parentWidth      = getmaxx (cdkscreen->window);
   int parentHeight     = getmaxy (cdkscreen->window);
   int fieldWidth       = fWidth;
   int boxWidth         = 0;
   int boxHeight;
   int xpos             = xplace;
   int ypos             = yplace;
   int junk             = 0;
   int horizontalAdjust, oldWidth;

   if ((entry = newCDKObject (CDKENTRY, &my_funcs)) == 0)
        return (0);

   setCDKEntryBox (entry, Box);
   boxHeight = (BorderOf (entry) * 2) + 1;

   /*
    * If the fieldWidth is a negative value, the fieldWidth will
    * be COLS-fieldWidth, otherwise, the fieldWidth will be the
    * given width.
    */
   fieldWidth = setWidgetDimension (parentWidth, fieldWidth, 0);
   boxWidth = fieldWidth + 2 * BorderOf (entry);

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

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

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

   boxHeight += TitleLinesOf (entry);

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

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

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

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

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

   /* Make room for the info char * pointer. */
   entry->info = typeMallocN (char, max + 3);
   if (entry->info == 0)
   {
      destroyCDKObject (entry);
      return (0);
   }
   cleanChar (entry->info, max + 3, '\0');
   entry->infoWidth = max + 3;

   /* *INDENT-EQLS* Set up the rest of the structure. */
   ScreenOf (entry)             = cdkscreen;
   entry->parent                = cdkscreen->window;
   entry->shadowWin             = 0;
   entry->fieldAttr             = fieldAttr;
   entry->fieldWidth            = fieldWidth;
   entry->filler                = filler;
   entry->hidden                = filler;
   ObjOf (entry)->inputWindow   = entry->fieldWin;
   ObjOf (entry)->acceptsFocus  = TRUE;
   ReturnOf (entry)             = NULL;
   entry->shadow                = shadow;
   entry->screenCol             = 0;
   entry->leftChar              = 0;
   entry->min                   = min;
   entry->max                   = max;
   entry->boxWidth              = boxWidth;
   entry->boxHeight             = boxHeight;
   initExitType (entry);
   entry->dispType              = dispType;
   entry->callbackfn            = CDKEntryCallBack;

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

   registerCDKObject (cdkscreen, vENTRY, entry);

   return (entry);
}

/*
 * This means you want to use the given entry field. It takes input
 * from the keyboard, and when its done, it fills the entry info
 * element of the structure with what was typed.
 */
char *activateCDKEntry (CDKENTRY *entry, chtype *actions)
{
   chtype input = 0;
   boolean functionKey;
   char *ret = 0;

   /* Draw the widget. */
   drawCDKEntry (entry, ObjOf (entry)->box);

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

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

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

   /* Make sure we return the correct info. */
   if (entry->exitType == vNORMAL)
   {
      return entry->info;
   }
   else
   {
      return 0;
   }
}

static void setPositionToEnd (CDKENTRY *entry)
{
   int stringLen;

   stringLen = (int)strlen (entry->info);
   if (stringLen >= entry->fieldWidth)
   {
      if (stringLen < entry->max)
      {
	 int charCount = entry->fieldWidth - 1;
	 entry->leftChar = stringLen - charCount;
	 entry->screenCol = charCount;
      }
      else
      {
	 entry->leftChar = stringLen - entry->fieldWidth;
	 entry->screenCol = stringLen - 1;
      }
   }
   else
   {
      entry->leftChar = 0;
      entry->screenCol = stringLen;
   }
}

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

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

   /* Refresh the widget field. */
   drawCDKEntryField (widget);

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

   /* Should we continue? */
   if (ppReturn != 0)
   {
      /* Check a predefined binding... */
      if (checkCDKObjectBind (vENTRY, widget, input) != 0)
      {
	 checkEarlyExit (widget);
	 complete = TRUE;
      }
      else
      {
	 int infoLength = (int)strlen (widget->info);
	 int currPos = widget->screenCol + widget->leftChar;

	 switch (input)
	 {
	 case KEY_UP:
	 case KEY_DOWN:
	    Beep ();
	    break;

	 case KEY_HOME:
	    widget->leftChar = 0;
	    widget->screenCol = 0;
	    drawCDKEntryField (widget);
	    break;

	 case CDK_TRANSPOSE:
	    if (currPos >= infoLength - 1)
	    {
	       Beep ();
	    }
	    else
	    {
	       char holder = widget->info[currPos];
	       widget->info[currPos] = widget->info[currPos + 1];
	       widget->info[currPos + 1] = holder;
	       drawCDKEntryField (widget);
	    }
	    break;

	 case KEY_END:
	    setPositionToEnd (widget);
	    drawCDKEntryField (widget);
	    break;

	 case KEY_LEFT:
	    if (currPos <= 0)
	    {
	       Beep ();
	    }
	    else if (widget->screenCol == 0)
	    {
	       /* Scroll left.  */
	       widget->leftChar--;
	       drawCDKEntryField (widget);
	    }
	    else
	    {
	       wmove (widget->fieldWin, 0, --widget->screenCol);
	    }
	    break;

	 case KEY_RIGHT:
	    if (currPos >= infoLength)
	    {
	       Beep ();
	    }
	    else if (widget->screenCol == widget->fieldWidth - 1)
	    {
	       /* Scroll to the right. */
	       widget->leftChar++;
	       drawCDKEntryField (widget);
	    }
	    else
	    {
	       /* Move right. */
	       wmove (widget->fieldWin, 0, ++widget->screenCol);
	    }
	    break;

	 case KEY_BACKSPACE:
	 case KEY_DC:
	    if (widget->dispType == vVIEWONLY)
	    {
	       Beep ();
	    }
	    else
	    {
	       bool success = FALSE;

	       if (input == KEY_BACKSPACE)
		  --currPos;

	       if (currPos >= 0 && infoLength > 0)
	       {
		  if (currPos < infoLength)
		  {
		     int x;

		     for (x = currPos; x < infoLength; x++)
		     {
			widget->info[x] = widget->info[x + 1];
		     }
		     success = TRUE;
		  }
		  else if (input == KEY_BACKSPACE)
		  {
		     widget->info[infoLength - 1] = '\0';
		     success = TRUE;
		  }
	       }

	       if (success)
	       {
		  if (input == KEY_BACKSPACE)
		  {
		     if (widget->screenCol > 0)
			widget->screenCol--;
		     else
			widget->leftChar--;
		  }

		  drawCDKEntryField (widget);
	       }
	       else
	       {
		  Beep ();
	       }
	    }
	    break;

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

	 case CDK_ERASE:
	    if (infoLength != 0)
	    {
	       cleanCDKEntry (widget);
	       drawCDKEntryField (widget);
	    }
	    break;

	 case CDK_CUT:
	    if (infoLength != 0)
	    {
	       freeChar (GPasteBuffer);
	       GPasteBuffer = copyChar (widget->info);
	       cleanCDKEntry (widget);
	       drawCDKEntryField (widget);
	    }
	    else
	    {
	       Beep ();
	    }
	    break;

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

	 case CDK_PASTE:
	    if (GPasteBuffer != 0)
	    {
	       setCDKEntryValue (widget, GPasteBuffer);
	       drawCDKEntryField (widget);
	    }
	    else
	    {
	       Beep ();
	    }
	    break;

	 case KEY_TAB:
	 case KEY_ENTER:
	    if (infoLength >= widget->min)
	    {
	       setExitType (widget, input);
	       ret = (widget->info);
	       complete = TRUE;
	    }
	    else
	    {
	       Beep ();
	    }
	    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 do a post-process? */
      if (!complete && (PostProcessFuncOf (widget) != 0))
      {
	 PostProcessFuncOf (widget) (vENTRY,
				     widget,
				     PostProcessDataOf (widget),
				     input);
      }
   }

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

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

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

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

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

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

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

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

/*
 * This is a generic character parser for the entry field. It is used as a
 * callback function, so any personal modifications can be made by creating
 * a new function and calling the activation with its name.
 */
static void CDKEntryCallBack (CDKENTRY *entry, chtype character)
{
   int plainchar = filterByDisplayType (entry->dispType, character);

   if (plainchar == ERR ||
       ((int)strlen (entry->info) >= entry->max))
   {
      Beep ();
   }
   else
   {
      /* Update the screen and pointer. */
      if (entry->screenCol != entry->fieldWidth - 1)
      {
	 int x;

	 for (x = (int)strlen (entry->info);
	      x > (entry->screenCol + entry->leftChar);
	      x--)
	 {
	    entry->info[x] = entry->info[x - 1];
	 }
	 entry->info[entry->screenCol + entry->leftChar] = (char)plainchar;
	 entry->screenCol++;
      }
      else
      {
	 /* Update the character pointer. */
	 size_t temp = strlen (entry->info);
	 entry->info[temp] = (char)plainchar;
	 entry->info[temp + 1] = '\0';
	 /* Do not update the pointer if it's the last character */
	 if ((int)(temp + 1) < entry->max)
	    entry->leftChar++;
      }

      /* Update the entry field. */
      drawCDKEntryField (entry);
   }
}

/*
 * This erases the information in the entry field
 * and redraws a clean and empty entry field.
 */
void cleanCDKEntry (CDKENTRY *entry)
{
   int width = entry->fieldWidth;

   /* Erase the information in the character pointer. */
   cleanChar (entry->info, entry->infoWidth, '\0');

   /* Clean the entry screen field. */
   (void)mvwhline (entry->fieldWin, 0, 0, entry->filler, width);

   /* Reset some variables. */
   entry->screenCol = 0;
   entry->leftChar = 0;

   /* Refresh the entry field. */
   wrefresh (entry->fieldWin);
}

/*
 * This draws the entry field.
 */
static void _drawCDKEntry (CDKOBJS *object, boolean Box)
{
   CDKENTRY *entry = (CDKENTRY *)object;

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

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

   drawCdkTitle (entry->win, object);

   wrefresh (entry->win);

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

   drawCDKEntryField (entry);
}

/*
 * This redraws the entry field.
 */
static void drawCDKEntryField (CDKENTRY *entry)
{
   int x = 0;

   /* Set background color and attributes of the entry field */
   wbkgd (entry->fieldWin, entry->fieldAttr);

   /* Draw in the filler characters. */
   (void)mvwhline (entry->fieldWin, 0, x, entry->filler | entry->fieldAttr, entry->fieldWidth);

   /* If there is information in the field. Then draw it in. */
   if (entry->info != 0)
   {
      int infoLength = (int)strlen (entry->info);

      /* Redraw the field. */
      if (isHiddenDisplayType (entry->dispType))
      {
	 for (x = entry->leftChar; x < infoLength; x++)
	 {
	    (void)mvwaddch (entry->fieldWin, 0, x - entry->leftChar,
			    entry->hidden | entry->fieldAttr);
	 }
      }
      else
      {
	 for (x = entry->leftChar; x < infoLength; x++)
	 {
	    (void)mvwaddch (entry->fieldWin, 0, x - entry->leftChar,
			    CharOf (entry->info[x]) | entry->fieldAttr);
	 }
      }
      wmove (entry->fieldWin, 0, entry->screenCol);
   }

   wrefresh (entry->fieldWin);
}

/*
 * This erases an entry widget from the screen.
 */
static void _eraseCDKEntry (CDKOBJS *object)
{
   if (validCDKObject (object))
   {
      CDKENTRY *entry = (CDKENTRY *)object;

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

/*
 * This destroys an entry widget.
 */
static void _destroyCDKEntry (CDKOBJS *object)
{
   if (object != 0)
   {
      CDKENTRY *entry = (CDKENTRY *)object;

      cleanCdkTitle (object);
      freeChtype (entry->label);
      freeChar (entry->info);

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

      /* Clean the key bindings. */
      cleanCDKObjectBindings (vENTRY, entry);

      /* Unregister this object. */
      unregisterCDKObject (vENTRY, entry);
   }
}

/*
 * This sets specific attributes of the entry field.
 */
void setCDKEntry (CDKENTRY *entry,
		  const char *value,
		  int min,
		  int max,
		  boolean Box GCC_UNUSED)
{
   setCDKEntryValue (entry, value);
   setCDKEntryMin (entry, min);
   setCDKEntryMax (entry, max);
}

/*
 * This removes the old information in the entry field and keeps the
 * new information given.
 */
void setCDKEntryValue (CDKENTRY *entry, const char *newValue)
{
   /* If the pointer sent in is the same pointer as before, do nothing. */
   if (entry->info != newValue)
   {
      /* Just to be sure, if lets make sure the new value isn't null. */
      if (newValue == 0)
      {
	 /* Then we want to just erase the old value. */
	 cleanChar (entry->info, entry->infoWidth, '\0');

	 /* Set the pointers back to zero. */
	 entry->leftChar = 0;
	 entry->screenCol = 0;
      }
      else
      {
	 /* Determine how many characters we need to copy. */
	 int copychars = MINIMUM ((int)strlen (newValue), entry->max);

	 /* OK, erase the old value, and copy in the new value. */
	 cleanChar (entry->info, entry->max, '\0');
	 strncpy (entry->info, newValue, (unsigned)copychars);

	 setPositionToEnd (entry);
      }
   }
}
char *getCDKEntryValue (CDKENTRY *entry)
{
   return entry->info;
}

/*
 * This sets the maximum length of the string that will be accepted.
 */
void setCDKEntryMax (CDKENTRY *entry, int max)
{
   entry->max = max;
}
int getCDKEntryMax (CDKENTRY *entry)
{
   return entry->max;
}

/*
 * This sets the minimum length of the string that will
 * be accepted.
 */
void setCDKEntryMin (CDKENTRY *entry, int min)
{
   entry->min = min;
}
int getCDKEntryMin (CDKENTRY *entry)
{
   return entry->min;
}

/*
 * This sets the filler character to be used in the entry field.
 */
void setCDKEntryFillerChar (CDKENTRY *entry, chtype fillerCharacter)
{
   entry->filler = fillerCharacter;
}
chtype getCDKEntryFillerChar (CDKENTRY *entry)
{
   return entry->filler;
}

/*
 * This sets the character to use when a hidden type is used.
 */
void setCDKEntryHiddenChar (CDKENTRY *entry, chtype hiddenCharacter)
{
   entry->hidden = hiddenCharacter;
}
chtype getCDKEntryHiddenChar (CDKENTRY *entry)
{
   return entry->hidden;
}

/*
 * This sets the widgets box attribute.
 */
void setCDKEntryBox (CDKENTRY *entry, boolean Box)
{
   ObjOf (entry)->box = Box;
   ObjOf (entry)->borderSize = Box ? 1 : 0;
}
boolean getCDKEntryBox (CDKENTRY *entry)
{
   return ObjOf (entry)->box;
}

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

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

/*
 * This sets the attribute of the entry field.
 */
void setCDKEntryHighlight (CDKENTRY *entry, chtype highlight, boolean cursor)
{
   wbkgd (entry->fieldWin, highlight);
   entry->fieldAttr = highlight;
   curs_set (cursor);
   /*
    *  FIXME -  if (cursor) { move the cursor to this widget }
    */
}

/*
 * This sets the entry field callback function.
 */
void setCDKEntryCB (CDKENTRY *entry, ENTRYCB callback)
{
   entry->callbackfn = callback;
}

static void _focusCDKEntry (CDKOBJS *object)
{
   CDKENTRY *entry = (CDKENTRY *)object;

   wmove (entry->fieldWin, 0, entry->screenCol);
   wrefresh (entry->fieldWin);
}

static void _unfocusCDKEntry (CDKOBJS *object)
{
   CDKENTRY *entry = (CDKENTRY *)object;

   drawCDKEntry (entry, ObjOf (entry)->box);
   wrefresh (entry->fieldWin);
}

#if 0
static void _refreshDataCDKEntry (CDKOBJS *object)
{
   CDKENTRY *entry = (CDKENTRY *)object;

   if (ReturnOf (entry))
   {
      switch (DataTypeOf (entry))
      {
      default:
      case DataTypeString:
	 strcpy (entry->info, (char *)ReturnOf (entry));
	 break;
      case DataTypeInt:
	 sprintf (entry->info, "%d", *((int *)ReturnOf (entry)));
	 break;
      case DataTypeFloat:
	 sprintf (entry->info, "%g", *((float *)ReturnOf (entry)));
	 break;
      case DataTypeDouble:
	 sprintf (entry->info, "%g", *((double *)ReturnOf (entry)));
	 break;
      }
      entry->screenCol = strlen (entry->info);
      drawCDKEntryField (entry);
   }
}

static void _saveDataCDKEntry (CDKOBJS *object)
{
   CDKENTRY *entry = (CDKENTRY *)object;

   if (ReturnOf (entry))
   {
      switch (DataTypeOf (entry))
      {
      default:
      case DataTypeString:
	 strcpy ((char *)ReturnOf (entry), entry->info);
	 break;
      case DataTypeInt:
	 *((int *)ReturnOf (entry)) = atoi (entry->info);
	 break;
      case DataTypeFloat:
	 *((float *)ReturnOf (entry)) = atof (entry->info);
	 break;
      case DataTypeDouble:
	 *((double *)ReturnOf (entry)) = atof (entry->info);
	 break;
      }
   }
}
#else
dummyRefreshData (Entry)
dummySaveData (Entry)
#endif
