#include <cdk_int.h> /* * $Author: tom $ * $Date: 2016/11/20 18:29:20 $ * $Revision: 1.28 $ */ /* * Declare file local prototypes. */ static void drawCDK<MIXED>Field (CDK<UPPER> *widget); static int formattedSize (CDK<UPPER> *widget, <CTYPE> value); DeclareCDKObjects (<UPPER>, <MIXED>, setCdk, <DTYPE>); /* * This function creates a widget. */ CDK<UPPER> *newCDK<MIXED> (CDKSCREEN *cdkscreen, int xplace, int yplace, const char *title, const char *label, chtype filler, int fieldWidth, <CTYPE> start, <CTYPE> low, <CTYPE> high, <CTYPE> inc, <CTYPE> fastInc, #if <FLOAT> int digits, #endif <FLOAT> boolean Box, boolean shadow) { /* *INDENT-EQLS* */ CDK<UPPER> *widget = 0; int parentWidth = getmaxx (cdkscreen->window); int parentHeight = getmaxy (cdkscreen->window); int boxHeight; int boxWidth = 0; int horizontalAdjust, oldWidth; int xpos = xplace; int ypos = yplace; int highValueLen; int x, junk; /* *INDENT-OFF* */ static const struct { int from; int to; } bindings[] = { { 'u', KEY_UP }, { 'U', KEY_PPAGE }, { CDK_BACKCHAR, KEY_PPAGE }, { CDK_FORCHAR, KEY_NPAGE }, { 'g', KEY_HOME }, { '^', KEY_HOME }, { 'G', KEY_END }, { '$', KEY_END }, }; /* *INDENT-ON* */ if ((widget = newCDKObject (CDK<UPPER>, &my_funcs)) == 0) return (0); setCDK<MIXED>Box (widget, Box); boxHeight = (BorderOf (widget) * 2) + 1; /* *INDENT-EQLS* Set some basic values of the widget's data field. */ widget->label = 0; widget->labelLen = 0; widget->labelWin = 0; #if <FLOAT> widget->digits = digits; #endif <FLOAT> highValueLen = MAXIMUM (formattedSize (widget, low), formattedSize (widget, high)); /* * 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); /* Translate the label char *pointer to a chtype pointer. */ if (label != 0) { widget->label = char2Chtype (label, &widget->labelLen, &junk); boxWidth = widget->labelLen + fieldWidth + highValueLen + 2 * BorderOf (widget); } else { boxWidth = fieldWidth + highValueLen + 2 * BorderOf (widget); } oldWidth = boxWidth; boxWidth = setCdkTitle (ObjOf (widget), title, boxWidth); horizontalAdjust = (boxWidth - oldWidth) / 2; boxHeight += TitleLinesOf (widget); /* * Make sure we didn't extend beyond the dimensions of the window. */ boxWidth = (boxWidth > parentWidth ? parentWidth : boxWidth); boxHeight = (boxHeight > parentHeight ? parentHeight : boxHeight); fieldWidth = (fieldWidth > (boxWidth - widget->labelLen - highValueLen - 1) ? (boxWidth - widget->labelLen - highValueLen - 1) : fieldWidth); /* Rejustify the x and y positions if we need to. */ alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight); /* Make the widget's window. */ widget->win = newwin (boxHeight, boxWidth, ypos, xpos); /* Is the main window null??? */ if (widget->win == 0) { destroyCDKObject (widget); return (0); } /* Create the widget's label window. */ if (widget->label != 0) { widget->labelWin = subwin (widget->win, 1, widget->labelLen, ypos + TitleLinesOf (widget) + BorderOf (widget), xpos + horizontalAdjust + BorderOf (widget)); if (widget->labelWin == 0) { destroyCDKObject (widget); return (0); } } /* Create the widget's data field window. */ widget->fieldWin = subwin (widget->win, 1, fieldWidth + highValueLen - 1, (ypos + TitleLinesOf (widget) + BorderOf (widget)), (xpos + widget->labelLen + horizontalAdjust + BorderOf (widget))); if (widget->fieldWin == 0) { destroyCDKObject (widget); return (0); } keypad (widget->fieldWin, TRUE); keypad (widget->win, TRUE); /* *INDENT-EQLS* Create the widget's data field. */ ScreenOf (widget) = cdkscreen; widget->parent = cdkscreen->window; widget->shadowWin = 0; widget->boxWidth = boxWidth; widget->boxHeight = boxHeight; widget->fieldWidth = fieldWidth - 1; widget->filler = filler; widget->low = low; widget->high = high; widget->current = start; widget->inc = inc; widget->fastinc = fastInc; initExitType (widget); ObjOf (widget)->acceptsFocus = TRUE; ObjOf (widget)->inputWindow = widget->win; widget->shadow = shadow; /* Set the start value. */ if (start < low) { widget->current = low; } /* Do we want a shadow??? */ if (shadow) { widget->shadowWin = newwin (boxHeight, boxWidth, ypos + 1, xpos + 1); if (widget->shadowWin == 0) { destroyCDKObject (widget); return (0); } } /* Setup the key bindings. */ for (x = 0; x < (int)SIZEOF (bindings); ++x) bindCDKObject (v<UPPER>, widget, (chtype)bindings[x].from, getcCDKBind, (void *)(long)bindings[x].to); registerCDKObject (cdkscreen, v<UPPER>, widget); return (widget); } /* * This allows the person to use the widget's data field. */ <CTYPE> activateCDK<MIXED> (CDK<UPPER> *widget, chtype *actions) { <CTYPE> ret; /* Draw the widget. */ drawCDK<MIXED> (widget, ObjOf (widget)->box); if (actions == 0) { chtype input = 0; boolean functionKey; for (;;) { input = (chtype)getchCDKObject (ObjOf (widget), &functionKey); /* Inject the character into the widget. */ ret = (<CTYPE>)injectCDK<MIXED> (widget, input); if (widget->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 = (<CTYPE>)injectCDK<MIXED> (widget, actions[x]); if (widget->exitType != vEARLY_EXIT) { return ret; } } } /* Set the exit type and return. */ setExitType (widget, 0); return unknown<DTYPE>; } /* * Check if the value lies outside the low/high range. If so, force it in. */ static void limitCurrentValue (CDK<UPPER> *widget) { if (widget->current < widget->low) { widget->current = widget->low; Beep (); } else if (widget->current > widget->high) { widget->current = widget->high; Beep (); } } /* * Move the cursor to the given edit-position. */ static int moveToEditPosition (CDK<UPPER> *widget, int newPosition) { return wmove (widget->fieldWin, 0, widget->fieldWidth + formattedSize (widget, widget->current) - newPosition); } /* * Check if the cursor is on a valid edit-position. This must be one of * the non-blank cells in the field. */ static int validEditPosition (CDK<UPPER> *widget, int newPosition) { chtype ch; if (newPosition <= 0 || newPosition >= widget->fieldWidth) return FALSE; if (moveToEditPosition (widget, newPosition) == ERR) return FALSE; ch = winch (widget->fieldWin); if (CharOf (ch) != ' ') return TRUE; if (newPosition > 1) { /* don't use recursion - only one level is wanted */ if (moveToEditPosition (widget, newPosition - 1) == ERR) return FALSE; ch = winch (widget->fieldWin); return CharOf (ch) != ' '; } return FALSE; } /* * Set the edit position. Normally the cursor is one cell to the right of * the editable field. Moving it left, over the field allows the user to * modify cells by typing in replacement characters for the field's value. */ static void setEditPosition (CDK<UPPER> *widget, int newPosition) { if (newPosition < 0) { Beep (); } else if (newPosition == 0) { widget->fieldEdit = newPosition; } else if (validEditPosition (widget, newPosition)) { widget->fieldEdit = newPosition; } else { Beep (); } } /* * Remove the character from the string at the given column, if it is blank. * Returns true if a change was made. */ static bool removeChar (char *string, int col) { bool result = FALSE; if ((col >= 0) && (string[col] != ' ')) { while (string[col] != '\0') { string[col] = string[col + 1]; ++col; } result = TRUE; } return result; } /* * Perform an editing function for the field. */ static bool performEdit (CDK<UPPER> *widget, chtype input) { bool result = FALSE; bool modify = TRUE; int base = widget->fieldWidth; int need = formattedSize (widget, widget->current); char *temp = (char *)malloc ((size_t) need + 5); char *data = temp; char test; int col = need - widget->fieldEdit; #if <FLOAT> double value; #define SCANF_FMT "%lg%c" #endif <FLOAT> #if <INT> <CTYPE> value; #define SCANF_FMT "%<PRINT>%c" #endif <INT> if (temp != 0) { int adj = (col < 0) ? (-col) : 0; if (adj) { memset (temp, ' ', (size_t) adj); temp += adj; } wmove (widget->fieldWin, 0, base); winnstr (widget->fieldWin, temp, need); strcpy (temp + need, " "); if (isChar (input)) /* replace the char at the cursor */ { temp[col] = (char)(input); } else if (input == KEY_BACKSPACE) /* delete the char before the cursor */ { modify = removeChar (temp, col - 1); } else if (input == KEY_DC) /* delete the char at the cursor */ { modify = removeChar (temp, col); } else { modify = FALSE; } if (modify && sscanf (temp, SCANF_FMT, &value, &test) == 2 && test == ' ' && value >= widget->low && value <= widget->high) { setCDK<MIXED>Value (widget, (<CTYPE>)value); result = TRUE; } free (data); } return result; } #define Decrement(value,by) if (value - by < value) value -= by #define Increment(value,by) if (value + by > value) value += by /* * This function injects a single character into the widget. */ static int _injectCDK<MIXED> (CDKOBJS *object, chtype input) { CDK<UPPER> *widget = (CDK<UPPER> *)object; int ppReturn = 1; <CTYPE> ret = unknown<DTYPE>; bool complete = FALSE; /* Set the exit type. */ setExitType (widget, 0); /* Draw the field. */ drawCDK<MIXED>Field (widget); /* Check if there is a pre-process function to be called. */ if (PreProcessFuncOf (widget) != 0) { /* Call the pre-process function. */ ppReturn = PreProcessFuncOf (widget) (v<UPPER>, widget, PreProcessDataOf (widget), input); } /* Should we continue? */ if (ppReturn != 0) { /* Check for a key binding. */ if (checkCDKObjectBind (v<UPPER>, widget, input) != 0) { checkEarlyExit (widget); complete = TRUE; } else { switch (input) { case KEY_LEFT: setEditPosition (widget, widget->fieldEdit + 1); break; case KEY_RIGHT: setEditPosition (widget, widget->fieldEdit - 1); break; case KEY_DOWN: Decrement (widget->current, widget->inc); break; case KEY_UP: Increment (widget->current, widget->inc); break; case KEY_PPAGE: Increment (widget->current, widget->fastinc); break; case KEY_NPAGE: Decrement (widget->current, widget->fastinc); break; case KEY_HOME: widget->current = widget->low; break; case KEY_END: widget->current = widget->high; break; case KEY_TAB: case KEY_ENTER: setExitType (widget, input); ret = (widget->current); 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: if (widget->fieldEdit) { if (!performEdit (widget, input)) Beep (); } else { /* * The cursor is not within the editable text. Interpret * input as commands. */ switch (input) { case 'd': case '-': return _injectCDK<MIXED> (object, KEY_DOWN); case '+': return _injectCDK<MIXED> (object, KEY_UP); case 'D': return _injectCDK<MIXED> (object, KEY_NPAGE); case '0': return _injectCDK<MIXED> (object, KEY_HOME); default: Beep (); break; } } break; } } limitCurrentValue (widget); /* Should we call a post-process? */ if (!complete && (PostProcessFuncOf (widget) != 0)) { PostProcessFuncOf (widget) (v<UPPER>, widget, PostProcessDataOf (widget), input); } } if (!complete) { drawCDK<MIXED>Field (widget); setExitType (widget, 0); } ResultOf (widget).value<DTYPE> = ret; return (ret != unknown<DTYPE>); } /* * This moves the widget's data field to the given location. */ static void _moveCDK<MIXED> (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag) { /* *INDENT-EQLS* */ CDK<UPPER> *widget = (CDK<UPPER> *)object; int currentX = getbegx (widget->win); int currentY = getbegy (widget->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 (widget->win) + xplace; ypos = getbegy (widget->win) + yplace; } /* Adjust the window if we need to. */ alignxy (WindowOf (widget), &xpos, &ypos, widget->boxWidth, widget->boxHeight); /* Get the difference. */ xdiff = currentX - xpos; ydiff = currentY - ypos; /* Move the window to the new location. */ moveCursesWindow (widget->win, -xdiff, -ydiff); moveCursesWindow (widget->labelWin, -xdiff, -ydiff); moveCursesWindow (widget->fieldWin, -xdiff, -ydiff); moveCursesWindow (widget->shadowWin, -xdiff, -ydiff); /* Touch the windows so they 'move'. */ refreshCDKWindow (WindowOf (widget)); /* Redraw the window, if they asked for it. */ if (refresh_flag) { drawCDK<MIXED> (widget, ObjOf (widget)->box); } } /* * This function draws the widget. */ static void _drawCDK<MIXED> (CDKOBJS *object, boolean Box) { CDK<UPPER> *widget = (CDK<UPPER> *)object; /* Draw the shadow. */ if (widget->shadowWin != 0) { drawShadow (widget->shadowWin); } /* Box the widget if asked. */ if (Box) { drawObjBox (widget->win, ObjOf (widget)); } drawCdkTitle (widget->win, object); /* Draw the label. */ if (widget->labelWin != 0) { writeChtype (widget->labelWin, 0, 0, widget->label, HORIZONTAL, 0, widget->labelLen); wrefresh (widget->labelWin); } wrefresh (widget->win); /* Draw the field window. */ drawCDK<MIXED>Field (widget); } /* * This draws the widget. */ static void drawCDK<MIXED>Field (CDK<UPPER> *widget) { int fillerCharacters, x; char temp[256]; double step = ((double)widget->fieldWidth / (double)(widget->high - widget->low)); /* Determine how many filler characters need to be drawn. */ fillerCharacters = (int)((widget->current - widget->low) * step); werase (widget->fieldWin); /* Add the character to the window. */ for (x = 0; x < fillerCharacters; x++) { (void)mvwaddch (widget->fieldWin, 0, x, widget->filler); } /* Draw the value in the field. */ #if <FLOAT> { char format[256]; int digits = MINIMUM (widget->digits, 30); sprintf (format, "%%.%i<PRINT>", digits); sprintf (temp, format, widget->current); } #endif <FLOAT> #if <INT> sprintf (temp, "%<PRINT>", widget->current); #endif <INT> writeCharAttrib (widget->fieldWin, widget->fieldWidth, 0, temp, A_NORMAL, HORIZONTAL, 0, (int)strlen (temp)); moveToEditPosition (widget, widget->fieldEdit); wrefresh (widget->fieldWin); } /* * This sets the background attribute of the widget. */ static void _setBKattr<MIXED> (CDKOBJS *object, chtype attrib) { if (object != 0) { CDK<UPPER> *widget = (CDK<UPPER> *)object; /* Set the widgets background attribute. */ wbkgd (widget->win, attrib); wbkgd (widget->fieldWin, attrib); if (widget->labelWin != 0) { wbkgd (widget->labelWin, attrib); } } } /* * This function destroys the widget. */ static void _destroyCDK<MIXED> (CDKOBJS *object) { if (object != 0) { CDK<UPPER> *widget = (CDK<UPPER> *)object; cleanCdkTitle (object); freeChtype (widget->label); /* Clean up the windows. */ deleteCursesWindow (widget->fieldWin); deleteCursesWindow (widget->labelWin); deleteCursesWindow (widget->shadowWin); deleteCursesWindow (widget->win); /* Clean the key bindings. */ cleanCDKObjectBindings (v<UPPER>, widget); /* Unregister this object. */ unregisterCDKObject (v<UPPER>, widget); } } /* * This function erases the widget from the screen. */ static void _eraseCDK<MIXED> (CDKOBJS *object) { if (validCDKObject (object)) { CDK<UPPER> *widget = (CDK<UPPER> *)object; eraseCursesWindow (widget->labelWin); eraseCursesWindow (widget->fieldWin); eraseCursesWindow (widget->win); eraseCursesWindow (widget->shadowWin); } } static int formattedSize (CDK<UPPER> *widget, <CTYPE> value) { char temp[256]; #if <FLOAT> char format[256]; int digits = MINIMUM (widget->digits, 30); sprintf (format, "%%.%i<PRINT>", digits); sprintf (temp, format, value); #endif <FLOAT> #if <INT> (void)widget; sprintf (temp, "%<PRINT>", value); #endif <INT> return (int) strlen (temp); } /* * This function sets the low/high/current values of the widget. */ void setCDK<MIXED> (CDK<UPPER> *widget, <CTYPE> low, <CTYPE> high, <CTYPE> value, boolean Box) { setCDK<MIXED>LowHigh (widget, low, high); setCDK<MIXED>Value (widget, value); setCDK<MIXED>Box (widget, Box); } /* * This sets the digits. */ #if <FLOAT> void setCDK<MIXED>Digits (CDK<UPPER> *widget, int digits) { widget->digits = MAXIMUM (0, digits); } int getCDK<MIXED>Digits (CDK<UPPER> *widget) { return widget->digits; } #endif <FLOAT> /* * This sets the widget's value. */ void setCDK<MIXED>Value (CDK<UPPER> *widget, <CTYPE> value) { widget->current = value; limitCurrentValue (widget); } <CTYPE> getCDK<MIXED>Value (CDK<UPPER> *widget) { return widget->current; } /* * This function sets the low/high values of the widget. */ void setCDK<MIXED>LowHigh (CDK<UPPER> *widget, <CTYPE> low, <CTYPE> high) { /* Make sure the values aren't out of bounds. */ if (low <= high) { widget->low = low; widget->high = high; } else { widget->low = high; widget->high = low; } /* Make sure the user hasn't done something silly. */ limitCurrentValue (widget); } <CTYPE> getCDK<MIXED>LowValue (CDK<UPPER> *widget) { return widget->low; } <CTYPE> getCDK<MIXED>HighValue (CDK<UPPER> *widget) { return widget->high; } /* * This sets the widget's box attribute. */ void setCDK<MIXED>Box (CDK<UPPER> *widget, boolean Box) { ObjOf (widget)->box = Box; ObjOf (widget)->borderSize = Box ? 1 : 0; } boolean getCDK<MIXED>Box (CDK<UPPER> *widget) { return ObjOf (widget)->box; } static void _focusCDK<MIXED> (CDKOBJS *object) { CDK<UPPER> *widget = (CDK<UPPER> *)object; drawCDK<MIXED> (widget, ObjOf (widget)->box); } static void _unfocusCDK<MIXED> (CDKOBJS *object) { CDK<UPPER> *widget = (CDK<UPPER> *)object; drawCDK<MIXED> (widget, ObjOf (widget)->box); } dummyRefreshData (<MIXED>) dummySaveData (<MIXED>)