NetBSD/dist/cdk/entry.c

995 lines
22 KiB
C

#include <cdk.h>
/*
* $Author: garbled $
* $Date: 2001/01/04 20:15:30 $
* $Revision: 1.2 $
*/
/*
* Declare file local prototypes.
*/
static void CDKEntryCallBack (CDKENTRY *entry, chtype character);
static void drawCDKEntryField (CDKENTRY *entry);
/*
* Declare file local variables.
*/
extern char *GPasteBuffer;
DeclareCDKObjects(my_funcs,Entry);
/*
* This creates a pointer to an entry widget.
*/
CDKENTRY *newCDKEntry (CDKSCREEN *cdkscreen, int xplace, int yplace, char *title, char *label, chtype fieldAttr, chtype filler, EDisplayType dispType, int fWidth, int min, int max, boolean Box, boolean shadow)
{
/* Set up some variables. */
CDKENTRY *entry = newCDKObject(CDKENTRY, &my_funcs);
chtype *holder = 0;
int parentWidth = getmaxx(cdkscreen->window);
int parentHeight = getmaxy(cdkscreen->window);
int fieldWidth = fWidth;
int boxWidth = 0;
int boxHeight = 3;
int maxWidth = INT_MIN;
int xpos = xplace;
int ypos = yplace;
int junk = 0;
int horizontalAdjust = 0;
char **temp = 0;
int x, len, junk2;
/*
* 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;
/* Set some basic values of the entry field. */
entry->label = 0;
entry->labelLen = 0;
entry->labelWin = 0;
entry->titleLines = 0;
/* Translate the label char *pointer to a chtype pointer. */
if (label != 0)
{
entry->label = char2Chtype (label, &entry->labelLen, &junk);
boxWidth += entry->labelLen;
}
/* Translate the char * items to chtype * */
if (title != 0)
{
/* We need to split the title on \n. */
temp = CDKsplitString (title, '\n');
entry->titleLines = CDKcountStrings (temp);
/* We need to determine the widest title line. */
for (x=0; x < entry->titleLines; x++)
{
holder = char2Chtype (temp[x], &len, &junk2);
maxWidth = MAXIMUM (maxWidth, len);
freeChtype (holder);
}
/*
* If one of the title lines is wider than the field and the label,
* the box width will expand to accomodate.
*/
if (maxWidth > boxWidth)
{
horizontalAdjust = (int)((maxWidth - boxWidth) / 2) + 1;
boxWidth = maxWidth + 2;
}
/* For each line in the title, convert from char * to chtype * */
for (x=0; x < entry->titleLines; x++)
{
entry->title[x] = char2Chtype (temp[x], &entry->titleLen[x], &entry->titlePos[x]);
entry->titlePos[x] = justifyString (boxWidth - 2, entry->titleLen[x], entry->titlePos[x]);
}
CDKfreeStrings(temp);
}
else
{
/* No title? Set the required variables. */
entry->titleLines = 0;
}
boxHeight += entry->titleLines;
/*
* Make sure we didn't extend beyond the dimensions of the window.
*/
boxWidth = MINIMUM (boxWidth, parentWidth);
boxHeight = MINIMUM (boxHeight, parentHeight);
fieldWidth = (fieldWidth > (boxWidth - entry->labelLen - 2) ? (boxWidth - entry->labelLen - 2) : fieldWidth);
/* 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 + !!shadow, boxWidth + !!shadow, ypos, xpos);
/* Is the window null? */
if (entry->win == 0)
{
/* Clean up the pointers. */
freeChtype (entry->label);
free (entry);
/* Exit with null. */
return (0);
}
keypad (entry->win, TRUE);
if (entry->titleLines > 0)
{
/* Make the title window. */
entry->titleWin = subwin (entry->win,
entry->titleLines, boxWidth - 2,
ypos + 1, xpos + 1);
}
/* Make the label win, if we need to. */
if (label != 0)
{
entry->labelWin = subwin (entry->win, 1, entry->labelLen,
ypos + entry->titleLines + 1,
xpos + horizontalAdjust + 1);
}
/* Make the field window. */
entry->fieldWin = subwin (entry->win, 1, fieldWidth,
ypos + entry->titleLines + 1,
xpos + entry->labelLen + horizontalAdjust + 1);
keypad (entry->fieldWin, TRUE);
/* Make room for the info char * pointer. */
entry->infoWidth = max + 3;
entry->info = (char *)malloc (entry->infoWidth);
cleanChar (entry->info, entry->infoWidth, '\0');
/* Set up the rest of the structure. */
ScreenOf(entry) = cdkscreen;
entry->parent = cdkscreen->window;
entry->fieldAttr = fieldAttr;
entry->fieldWidth = fieldWidth;
entry->filler = filler;
entry->hidden = filler;
ObjOf(entry)->box = Box;
entry->shadow = shadow;
entry->screenCol = 0;
entry->leftChar = 0;
entry->min = min;
entry->max = max;
entry->boxWidth = boxWidth;
entry->boxHeight = boxHeight;
entry->ULChar = ACS_ULCORNER;
entry->URChar = ACS_URCORNER;
entry->LLChar = ACS_LLCORNER;
entry->LRChar = ACS_LRCORNER;
entry->HChar = ACS_HLINE;
entry->VChar = ACS_VLINE;
entry->BoxAttrib = A_NORMAL;
entry->exitType = vNEVER_ACTIVATED;
entry->dispType = dispType;
entry->callbackfn = (void *)&CDKEntryCallBack;
entry->preProcessFunction = 0;
entry->preProcessData = 0;
entry->postProcessFunction = 0;
entry->postProcessData = 0;
/* Clean the key bindings. */
cleanCDKObjectBindings (vENTRY, entry);
/* Register this baby. */
registerCDKObject (cdkscreen, vENTRY, entry);
/* Return the pointer to the structure. */
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)
{
/* Declare local variables. */
chtype input = 0;
char *ret = 0;
/* Draw the widget. */
drawCDKEntry (entry, ObjOf(entry)->box);
/* Check if 'actions' is null. */
if (actions == 0)
{
for (;;)
{
/* Get the input. */
wrefresh (entry->fieldWin);
input = wgetch (entry->fieldWin);
/* Inject the character into the widget. */
ret = injectCDKEntry (entry, input);
if (entry->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 = 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;
}
}
/*
* This injects a single character into the widget.
*/
char *injectCDKEntry (CDKENTRY *entry, chtype input)
{
/* Declare local variables. */
int ppReturn = 1;
int temp, x, charCount, stringLen;
char holder;
/* Check if there is a pre-process function to be called. */
if (entry->preProcessFunction != 0)
{
/* Call the pre-process function. */
ppReturn = ((PROCESSFN)(entry->preProcessFunction)) (vENTRY, entry, entry->preProcessData, input);
}
/* Should we continue? */
if (ppReturn != 0)
{
/* Check a predefined binding... */
if (checkCDKObjectBind (vENTRY, entry, input) != 0)
{
entry->exitType = vEARLY_EXIT;
return 0;
}
else
{
switch (input)
{
case KEY_UP :
case KEY_DOWN :
Beep();
break;
case CDK_BEGOFLINE :
entry->leftChar = 0;
entry->screenCol = 0;
break;
case CDK_TRANSPOSE :
stringLen = (int)strlen (entry->info);
temp = entry->leftChar + entry->screenCol;
if (temp >= stringLen-1)
{
Beep();
}
else
{
holder = entry->info[temp];
entry->info[temp] = entry->info[temp + 1];
entry->info[temp + 1] = holder;
}
break;
case CDK_ENDOFLINE :
stringLen = (int)strlen (entry->info);
if (stringLen >= entry->fieldWidth)
{
charCount = (int)(entry->fieldWidth * .8);
entry->leftChar = stringLen - charCount;
entry->screenCol = charCount;
}
else
{
entry->leftChar = 0;
entry->screenCol = stringLen;
}
break;
case KEY_LEFT :
case CDK_BACKCHAR :
if (entry->screenCol == 0)
{
if (entry->leftChar == 0)
{
Beep();
}
else
{
/* Scroll left. */
entry->leftChar--;
}
}
else
{
--entry->screenCol;
}
break;
case KEY_RIGHT :
case CDK_FORCHAR :
temp = (int)strlen (entry->info);
if (entry->screenCol == entry->fieldWidth-1)
{
if ((entry->leftChar + entry->screenCol + 1) == temp)
{
Beep();
}
else
{
/* Scroll to the right. */
entry->leftChar++;
}
}
else
{
/* Move right. */
if ((entry->leftChar + entry->screenCol) == temp)
{
Beep();
}
else
{
++entry->screenCol;
}
}
break;
case DELETE :
case CONTROL('H') :
case KEY_BACKSPACE :
case KEY_DC :
if (entry->dispType == vVIEWONLY)
{
Beep();
}
else
{
/* Get the length of the widget information. */
int infoLength = (int)strlen (entry->info);
if ((entry->leftChar + entry->screenCol) < infoLength)
{
/* We are deleteing from inside the string. */
int currPos = entry->screenCol + entry->leftChar;
for (x=currPos; x < infoLength; x++)
{
entry->info[x] = entry->info[x + 1];
}
entry->info[infoLength] = '\0';
}
else
{
/* We are deleting from the end of the string. */
if (infoLength > 0)
{
/* Update the character pointer. */
entry->info[infoLength-1] = '\0';
infoLength--;
/* Adjust the cursor. */
if (entry->screenCol > 0)
{
entry->screenCol--;
}
/*
* If we deleted the last character on the
* screen and the information has scrolled,
* adjust the entry field to show the info.
*/
if (entry->leftChar > 0 && entry->screenCol == 1)
{
if (infoLength < entry->fieldWidth-1)
{
entry->leftChar = 0;
entry->screenCol = infoLength;
}
else
{
entry->leftChar -= (entry->fieldWidth-3);
entry->screenCol = entry->fieldWidth-2;
}
}
}
else
{
Beep();
}
}
}
break;
case KEY_ESC :
entry->exitType = vESCAPE_HIT;
return 0;
case CDK_ERASE :
if ((int)strlen(entry->info) != 0)
{
cleanCDKEntry (entry);
}
break;
case CDK_CUT:
if ((int)strlen(entry->info) != 0)
{
freeChar (GPasteBuffer);
GPasteBuffer = copyChar (entry->info);
cleanCDKEntry (entry);
}
else
{
Beep();
}
break;
case CDK_COPY:
if ((int)strlen(entry->info) != 0)
{
freeChar (GPasteBuffer);
GPasteBuffer = copyChar (entry->info);
}
else
{
Beep();
}
break;
case CDK_PASTE:
if (GPasteBuffer != 0)
{
setCDKEntryValue (entry, GPasteBuffer);
}
else
{
Beep();
}
break;
case KEY_RETURN : case KEY_TAB : case KEY_ENTER : case KEY_CR :
if ((int)strlen (entry->info) >= entry->min)
{
entry->exitType = vNORMAL;
return (entry->info);
}
else
{
Beep();
}
break;
case CDK_REFRESH :
eraseCDKScreen (ScreenOf(entry));
refreshCDKScreen (ScreenOf(entry));
break;
default :
((ENTRYCB)entry->callbackfn)(entry, input);
break;
}
}
/* Should we do a post-process? */
if (entry->postProcessFunction != 0)
{
((PROCESSFN)(entry->postProcessFunction)) (vENTRY, entry, entry->postProcessData, input);
}
}
/* Refresh the entry field. */
drawCDKEntryField (entry);
/* Return and indicate that we exited early. */
entry->exitType = vEARLY_EXIT;
return 0;
}
/*
* This moves the entry field to the given location.
*/
static void _moveCDKEntry (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag)
{
CDKENTRY *entry = (CDKENTRY *)object;
/*
* If this is a relative move, then we will adjust where we want
* to move to.
*/
if (relative)
{
xplace += getbegx(entry->win);
yplace += getbegy(entry->win);
}
/* Adjust the window if we need to. */
alignxy (WindowOf(entry), &xplace, &yplace, entry->boxWidth, entry->boxHeight);
/* Move the window to the new location. */
moveCursesWindow(entry->win, xplace, yplace);
/* 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)
{
/* Declare local variables. */
char plainchar = (character & A_CHARTEXT);
int temp, x;
/* Start checking the input. */
if ((entry->dispType == vINT ||
entry->dispType == vHINT) &&
!isdigit((int)plainchar))
{
Beep();
}
else if ((entry->dispType == vCHAR ||
entry->dispType == vUCHAR ||
entry->dispType == vLCHAR ||
entry->dispType == vUHCHAR ||
entry->dispType == vLHCHAR) &&
isdigit((int)plainchar))
{
Beep();
}
else if (entry->dispType == vVIEWONLY)
{
Beep();
}
else
{
if ((int)strlen (entry->info) == entry->max)
{
Beep();
}
else
{
/* We will make any adjustments to the case of the character. */
if ((entry->dispType == vUCHAR ||
entry->dispType == vUHCHAR ||
entry->dispType == vUMIXED ||
entry->dispType == vUHMIXED)
&& !isdigit((int)plainchar))
{
plainchar = toupper (plainchar);
}
else if ((entry->dispType == vLCHAR ||
entry->dispType == vLHCHAR ||
entry->dispType == vLMIXED ||
entry->dispType == vLHMIXED) &&
!isdigit((int)plainchar))
{
plainchar = tolower (plainchar);
}
/* Update the screen and pointer. */
if (entry->screenCol != entry->fieldWidth-1)
{
/* Update the character pointer. */
temp = (int)strlen (entry->info);
for (x=temp; x > entry->screenCol + entry->leftChar; x--)
{
entry->info[x] = entry->info[x-1];
}
entry->info[entry->screenCol + entry->leftChar] = plainchar;
entry->screenCol++;
}
else
{
/* Update the character pointer. */
temp = (int)strlen (entry->info);
entry->info[temp] = plainchar;
entry->info[temp + 1] = '\0';
entry->leftChar++;
}
}
}
}
/*
* This erases the information in the entry field
* and redraws a clean and empty entry field.
*/
void cleanCDKEntry (CDKENTRY *entry)
{
/* Erase the information in the character pointer. */
cleanChar (entry->info, entry->infoWidth, '\0');
/* Clean the entry screen field. */
mvwhline (entry->fieldWin, 0, 0, entry->filler, entry->fieldWidth);
/* Reset some variables. */
entry->screenCol = 0;
entry->leftChar = 0;
/* Refresh the entry field. */
wmove (entry->fieldWin, 0, entry->screenCol);
wnoutrefresh (entry->fieldWin);
wnoutrefresh (entry->win);
}
/*
* This draws the entry field.
*/
static void _drawCDKEntry (CDKOBJS *object, boolean Box)
{
CDKENTRY *entry = (CDKENTRY *)object;
int x;
/* Box the widget if asked. */
if (Box)
{
attrbox (entry->win,
entry->ULChar, entry->URChar,
entry->LLChar, entry->LRChar,
entry->HChar, entry->VChar,
entry->BoxAttrib,
entry->shadow);
}
if (entry->titleLines > 0)
{
/* Draw in the title if there is one. */
for (x=0; x < entry->titleLines; x++)
{
writeChtype (entry->titleWin,
entry->titlePos[x], x,
entry->title[x],
HORIZONTAL, 0,
entry->titleLen[x]);
}
wnoutrefresh (entry->titleWin);
}
/* Draw in the label to the widget. */
if (entry->label != 0)
{
writeChtype (entry->labelWin, 0, 0,
entry->label,
HORIZONTAL, 0,
entry->labelLen);
wnoutrefresh (entry->labelWin);
}
/* Redraw the entry field. */
drawCDKEntryField (entry);
}
/*
* This redraws the entry field.
*/
static void drawCDKEntryField (CDKENTRY *entry)
{
/* Declare variables. */
int infoLength;
/* Draw in the filler characters.*/
mvwhline (entry->fieldWin, 0, 0, entry->filler, entry->fieldWidth);
/* If there is information in the field. Then draw it in. */
if (entry->info != 0)
{
infoLength = (int)strlen (entry->info);
/* Redraw the field. */
if (entry->dispType == vHINT ||
entry->dispType == vHCHAR ||
entry->dispType == vHMIXED ||
entry->dispType == vUHCHAR ||
entry->dispType == vLHCHAR ||
entry->dispType == vUHMIXED ||
entry->dispType == vLHMIXED)
{
mvwhline (entry->fieldWin, 0, 0, entry->hidden,
infoLength - entry->leftChar);
}
else
{
writeCharAttrib (entry->fieldWin, 0, 0, entry->info,
entry->fieldAttr, HORIZONTAL, entry->leftChar,
infoLength);
}
}
/* Refresh the entry field. */
wmove (entry->fieldWin, 0, entry->screenCol);
wnoutrefresh (entry->fieldWin);
wnoutrefresh (entry->win);
}
/*
* This erases an entry widget from the screen.
*/
static void _eraseCDKEntry (CDKOBJS *object)
{
CDKENTRY *entry = (CDKENTRY *)object;
eraseCursesWindow (entry->win);
}
/*
* This destroys an entry widget.
*/
void destroyCDKEntry (CDKENTRY *entry)
{
int x;
/* Erase the object. */
eraseCDKEntry (entry);
/* Clear out the character pointers. */
for (x=0; x < entry->titleLines; x++)
{
freeChtype (entry->title[x]);
}
freeChtype (entry->label);
freeChar (entry->info);
/* Delete the windows. */
deleteCursesWindow (entry->win);
/* Unregister this object. */
unregisterCDKObject (vENTRY, entry);
/* Finish cleaning up. */
free (entry);
}
/*
* This sets specific attributes of the entry field.
*/
void setCDKEntry (CDKENTRY *entry, char *value, int min, int max, boolean Box)
{
setCDKEntryValue (entry, value);
setCDKEntryMin (entry, min);
setCDKEntryMax (entry, max);
setCDKEntryBox (entry, Box);
}
/*
* This removes the old information in the entry field and keeps the
* new information given.
*/
void setCDKEntryValue (CDKENTRY *entry, char *newValue)
{
/* Declare local variables. */
int copychars = 0;
int stringLen = 0;
int charCount = 0;
/* If the pointer sent in is the same pointer as before, do nothing. */
if (entry->info == newValue)
{
return;
}
/* 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;
return;
}
/* Determine how many characters we need to copy. */
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, copychars);
stringLen = (int)strlen (entry->info);
/* Now determine the values of leftChar and screenCol. */
if (stringLen >= entry->fieldWidth)
{
charCount = (int)(entry->fieldWidth * .8);
entry->leftChar = stringLen - charCount;
entry->screenCol = charCount;
}
else
{
entry->leftChar = 0;
entry->screenCol = stringLen;
}
}
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 whether or not the entry field will be boxed.
*/
void setCDKEntryBox (CDKENTRY *entry, boolean Box)
{
ObjOf(entry)->box = Box;
}
boolean getCDKEntryBox (CDKENTRY *entry)
{
return ObjOf(entry)->box;
}
/*
* These functions set the drawing characters of the widget.
*/
void setCDKEntryULChar (CDKENTRY *entry, chtype character)
{
entry->ULChar = character;
}
void setCDKEntryURChar (CDKENTRY *entry, chtype character)
{
entry->URChar = character;
}
void setCDKEntryLLChar (CDKENTRY *entry, chtype character)
{
entry->LLChar = character;
}
void setCDKEntryLRChar (CDKENTRY *entry, chtype character)
{
entry->LRChar = character;
}
void setCDKEntryVerticalChar (CDKENTRY *entry, chtype character)
{
entry->VChar = character;
}
void setCDKEntryHorizontalChar (CDKENTRY *entry, chtype character)
{
entry->HChar = character;
}
void setCDKEntryBoxAttribute (CDKENTRY *entry, chtype character)
{
entry->BoxAttrib = character;
}
/*
* This sets the background color of the widget.
*/
void setCDKEntryBackgroundColor (CDKENTRY *entry, 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 (entry->win, holder[0]);
wbkgd (entry->fieldWin, holder[0]);
if (entry->label != 0)
{
wbkgd (entry->labelWin, holder[0]);
}
/* Clean up. */
freeChtype (holder);
}
/*
* This sets the entry field callback function.
*/
void setCDKEntryCB (CDKENTRY *entry, ENTRYCB callback)
{
entry->callbackfn = (void *)callback;
}
/*
* This function sets the pre-process function.
*/
void setCDKEntryPreProcess (CDKENTRY *entry, PROCESSFN callback, void *data)
{
entry->preProcessFunction = callback;
entry->preProcessData = data;
}
/*
* This function sets the post-process function.
*/
void setCDKEntryPostProcess (CDKENTRY *entry, PROCESSFN callback, void *data)
{
entry->postProcessFunction = callback;
entry->postProcessData = data;
}