1 #include <cdk_int.h>
2
3 /*
4 * $Author: tom $
5 * $Date: 2016/11/20 18:32:34 $
6 * $Revision: 1.25 $
7 */
8
9 /*
10 * Declare file local prototypes.
11 */
12 static void drawCDK<MIXED>Field (CDK<UPPER> * widget);
13
14 DeclareCDKObjects (<UPPER>, <MIXED>, setCdk, <DTYPE>);
15
16 /*
17 * This function creates a widget.
18 */
19 CDK<UPPER> *newCDK<MIXED> (CDKSCREEN *cdkscreen,
20 int xplace,
21 int yplace,
22 const char *title,
23 const char *label,
24 chtype fieldAttr,
25 int fieldWidth,
26 <CTYPE> start,
27 <CTYPE> low,
28 <CTYPE> high,
29 <CTYPE> inc,
30 <CTYPE> fastInc,
31 #if <FLOAT>
32 int digits,
33 #endif <FLOAT>
34 boolean Box,
35 boolean shadow)
36 {
37 /* *INDENT-EQLS* */
38 CDK<UPPER> *widget = 0;
39 int parentWidth = getmaxx (cdkscreen->window);
40 int parentHeight = getmaxy (cdkscreen->window);
41 int boxHeight;
42 int boxWidth;
43 int horizontalAdjust, oldWidth;
44 int xpos = xplace;
45 int ypos = yplace;
46 int x, junk;
47 /* *INDENT-OFF* */
48 static const struct { int from; int to; } bindings[] = {
49 { 'u', KEY_UP },
50 { 'U', KEY_PPAGE },
51 { CDK_BACKCHAR, KEY_PPAGE },
52 { CDK_FORCHAR, KEY_NPAGE },
53 { 'g', KEY_HOME },
54 { '^', KEY_HOME },
55 { 'G', KEY_END },
56 { '$', KEY_END },
57 };
58 /* *INDENT-ON* */
59
60
61 if ((widget = newCDKObject (CDK<UPPER>, &my_funcs)) == 0)
62 return (0);
63
64 setCDK<MIXED>Box (widget, Box);
65
66 boxHeight = (BorderOf (widget) * 2) + 1;
67
68 /* Set some basic values of the widget's data field. */
69 widget->label = 0;
70 widget->labelLen = 0;
71 widget->labelWin = 0;
72
73 /*
74 * If the fieldWidth is a negative value, the fieldWidth will
75 * be COLS-fieldWidth, otherwise, the fieldWidth will be the
76 * given width.
77 */
78 fieldWidth = setWidgetDimension (parentWidth, fieldWidth, 0);
79 boxWidth = fieldWidth + 2 * BorderOf (widget);
80
81 /* Translate the label char *pointer to a chtype pointer. */
82 if (label != 0)
83 {
84 widget->label = char2Chtype (label, &widget->labelLen, &junk);
85 boxWidth = widget->labelLen + fieldWidth + 2;
86 }
87
88 oldWidth = boxWidth;
89 boxWidth = setCdkTitle (ObjOf (widget), title, boxWidth);
90 horizontalAdjust = (boxWidth - oldWidth) / 2;
91
92 boxHeight += TitleLinesOf (widget);
93
94 /*
95 * Make sure we didn't extend beyond the dimensions of the window.
96 */
97 boxWidth = (boxWidth > parentWidth ? parentWidth : boxWidth);
98 boxHeight = (boxHeight > parentHeight ? parentHeight : boxHeight);
99 fieldWidth = (fieldWidth > (boxWidth - widget->labelLen - 2 * BorderOf (widget))
100 ? (boxWidth - widget->labelLen - 2 * BorderOf (widget))
101 : fieldWidth);
102
103 /* Rejustify the x and y positions if we need to. */
104 alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight);
105
106 /* Make the widget's window. */
107 widget->win = newwin (boxHeight, boxWidth, ypos, xpos);
108
109 /* Is the main window null??? */
110 if (widget->win == 0)
111 {
112 destroyCDKObject (widget);
113 return (0);
114 }
115
116 /* Create the widget's label window. */
117 if (widget->label != 0)
118 {
119 widget->labelWin = subwin (widget->win,
120 1, widget->labelLen,
121 ypos + TitleLinesOf (widget) + BorderOf (widget),
122 xpos + horizontalAdjust + BorderOf (widget));
123 if (widget->labelWin == 0)
124 {
125 destroyCDKObject (widget);
126 return (0);
127 }
128 }
129
130 /* Create the widget's data field window. */
131 widget->fieldWin = subwin (widget->win,
132 1, fieldWidth,
133 (ypos + TitleLinesOf (widget) + BorderOf (widget)),
134 (xpos
135 + widget->labelLen
136 + horizontalAdjust
137 + BorderOf (widget)));
138 if (widget->fieldWin == 0)
139 {
140 destroyCDKObject (widget);
141 return (0);
142 }
143 keypad (widget->fieldWin, TRUE);
144 keypad (widget->win, TRUE);
145
146 /* *INDENT-EQLS* Create the widget's data field. */
147 ScreenOf (widget) = cdkscreen;
148 widget->parent = cdkscreen->window;
149 widget->shadowWin = 0;
150 widget->boxWidth = boxWidth;
151 widget->boxHeight = boxHeight;
152 widget->fieldWidth = fieldWidth;
153 widget->fieldAttr = (chtype)fieldAttr;
154 widget->current = low;
155 widget->low = low;
156 widget->high = high;
157 widget->current = start;
158 widget->inc = inc;
159 widget->fastinc = fastInc;
160 #if <FLOAT>
161 widget->digits = digits;
162 #endif <FLOAT>
163 initExitType (widget);
164 ObjOf (widget)->acceptsFocus = TRUE;
165 ObjOf (widget)->inputWindow = widget->win;
166 widget->shadow = shadow;
167
168 /* Do we want a shadow??? */
169 if (shadow)
170 {
171 widget->shadowWin = newwin (boxHeight, boxWidth, ypos + 1, xpos + 1);
172 if (widget->shadowWin == 0)
173 {
174 destroyCDKObject (widget);
175 return (0);
176 }
177 }
178
179 /* Setup the key bindings. */
180 for (x = 0; x < (int)SIZEOF (bindings); ++x)
181 bindCDKObject (v<UPPER>,
182 widget,
183 (chtype) bindings[x].from,
184 getcCDKBind,
185 (void *)(long)bindings[x].to);
186
187 registerCDKObject (cdkscreen, v<UPPER>, widget);
188
189 return (widget);
190 }
191
192 /*
193 * This allows the person to use the widget's data field.
194 */
195 <CTYPE> activateCDK<MIXED> (CDK<UPPER> * widget, chtype *actions)
196 {
197 <CTYPE> ret;
198
199 /* Draw the widget. */
200 drawCDK<MIXED> (widget, ObjOf (widget)->box);
201
202 if (actions == 0)
203 {
204 chtype input = 0;
205 boolean functionKey;
206
207 for (;;)
208 {
209 input = (chtype) getchCDKObject (ObjOf (widget), &functionKey);
210
211 /* Inject the character into the widget. */
212 ret = (<CTYPE>) injectCDK<MIXED> (widget, input);
213 if (widget->exitType != vEARLY_EXIT)
214 {
215 return ret;
216 }
217 }
218 }
219 else
220 {
221 int length = chlen (actions);
222 int x = 0;
223
224 /* Inject each character one at a time. */
225 for (x = 0; x < length; x++)
226 {
227 ret = (<CTYPE>) injectCDK<MIXED> (widget, actions[x]);
228 if (widget->exitType != vEARLY_EXIT)
229 {
230 return ret;
231 }
232 }
233 }
234
235 /* Set the exit type and return. */
236 setExitType (widget, 0);
237 return unknown<DTYPE>;
238 }
239
240 /*
241 * Check if the value lies outside the low/high range. If so, force it in.
242 */
limitCurrentValue(CDK<UPPER> * widget)243 static void limitCurrentValue (CDK<UPPER> * widget)
244 {
245 if (widget->current < widget->low)
246 {
247 widget->current = widget->low;
248 Beep ();
249 }
250 else if (widget->current > widget->high)
251 {
252 widget->current = widget->high;
253 Beep ();
254 }
255 }
256
257 /*
258 * Move the cursor to the given edit-position.
259 */
moveToEditPosition(CDK<UPPER> * widget,int newPosition)260 static int moveToEditPosition (CDK<UPPER> * widget, int newPosition)
261 {
262 return wmove (widget->fieldWin, 0, widget->fieldWidth - newPosition - 1);
263 }
264
265 /*
266 * Check if the cursor is on a valid edit-position. This must be one of
267 * the non-blank cells in the field.
268 */
validEditPosition(CDK<UPPER> * widget,int newPosition)269 static int validEditPosition (CDK<UPPER> * widget, int newPosition)
270 {
271 chtype ch;
272 if (newPosition <= 0 || newPosition >= widget->fieldWidth)
273 return FALSE;
274 if (moveToEditPosition (widget, newPosition) == ERR)
275 return FALSE;
276 ch = winch (widget->fieldWin);
277 if (CharOf (ch) != ' ')
278 return TRUE;
279 if (newPosition > 1)
280 {
281 /* don't use recursion - only one level is wanted */
282 if (moveToEditPosition (widget, newPosition - 1) == ERR)
283 return FALSE;
284 ch = winch (widget->fieldWin);
285 return CharOf (ch) != ' ';
286 }
287 return FALSE;
288 }
289
290 /*
291 * Set the edit position. Normally the cursor is one cell to the right of
292 * the editable field. Moving it left, over the field allows the user to
293 * modify cells by typing in replacement characters for the field's value.
294 */
setEditPosition(CDK<UPPER> * widget,int newPosition)295 static void setEditPosition (CDK<UPPER> * widget, int newPosition)
296 {
297 if (newPosition < 0)
298 {
299 Beep ();
300 }
301 else if (newPosition == 0)
302 {
303 widget->fieldEdit = newPosition;
304 }
305 else if (validEditPosition (widget, newPosition))
306 {
307 widget->fieldEdit = newPosition;
308 }
309 else
310 {
311 Beep ();
312 }
313 }
314
315 /*
316 * Remove the character from the string at the given column, if it is blank.
317 * Returns true if a change was made.
318 */
removeChar(char * string,int col)319 static bool removeChar (char *string, int col)
320 {
321 bool result = FALSE;
322
323 if ((col >= 0) && (string[col] != ' '))
324 {
325 while (string[col] != '\0')
326 {
327 string[col] = string[col + 1];
328 ++col;
329 }
330 result = TRUE;
331 }
332 return result;
333 }
334
335 /*
336 * Perform an editing function for the field.
337 */
performEdit(CDK<UPPER> * widget,chtype input)338 static bool performEdit (CDK<UPPER> * widget, chtype input)
339 {
340 bool result = FALSE;
341 bool modify = TRUE;
342 int need = widget->fieldWidth;
343 char *temp = (char *)malloc ((size_t) need + 2);
344 char test;
345 int col = need - widget->fieldEdit - 1;
346 #if <FLOAT>
347 double value;
348 #define SCANF_FMT "%lg%c"
349 #endif <FLOAT>
350 #if <INT>
351 <CTYPE> value;
352 #define SCANF_FMT "%<PRINT>%c"
353 #endif <INT>
354
355 if (temp != 0)
356 {
357 int base = 0;
358
359 wmove (widget->fieldWin, 0, base);
360 winnstr (widget->fieldWin, temp, need);
361 strcpy (temp + need, " ");
362 if (isChar (input)) /* replace the char at the cursor */
363 {
364 temp[col] = (char) (input);
365 }
366 else if (input == KEY_BACKSPACE) /* delete the char before the cursor */
367 {
368 modify = removeChar (temp, col - 1);
369 }
370 else if (input == KEY_DC) /* delete the char at the cursor */
371 {
372 modify = removeChar (temp, col);
373 }
374 else
375 {
376 modify = FALSE;
377 }
378 if (modify
379 && sscanf (temp, SCANF_FMT, &value, &test) == 2
380 && test == ' '
381 && value >= widget->low
382 && value <= widget->high)
383 {
384 setCDK<MIXED>Value (widget, (<CTYPE>) value);
385 result = TRUE;
386 }
387 free (temp);
388 }
389 return result;
390 }
391
392 #define Decrement(value,by) if (value - by < value) value -= by
393 #define Increment(value,by) if (value + by > value) value += by
394
395 /*
396 * This function injects a single character into the widget.
397 */
398 static int _injectCDK<MIXED> (CDKOBJS *object, chtype input)
399 {
400 CDK<UPPER> *widget = (CDK<UPPER> *) object;
401 int ppReturn = 1;
402 <CTYPE> ret = unknown<DTYPE>;
403 bool complete = FALSE;
404
405 /* Set the exit type. */
406 setExitType (widget, 0);
407
408 /* Draw the field. */
409 drawCDK<MIXED>Field (widget);
410
411 /* Check if there is a pre-process function to be called. */
412 if (PreProcessFuncOf (widget) != 0)
413 {
414 /* Call the pre-process function. */
415 ppReturn = PreProcessFuncOf (widget) (v<UPPER>,
416 widget,
417 PreProcessDataOf (widget),
418 input);
419 }
420
421 /* Should we continue? */
422 if (ppReturn != 0)
423 {
424 /* Check for a key binding. */
425 if (checkCDKObjectBind (v<UPPER>, widget, input) != 0)
426 {
427 checkEarlyExit (widget);
428 complete = TRUE;
429 }
430 else
431 {
432 switch (input)
433 {
434 case KEY_LEFT:
435 setEditPosition (widget, widget->fieldEdit + 1);
436 break;
437
438 case KEY_RIGHT:
439 setEditPosition (widget, widget->fieldEdit - 1);
440 break;
441
442 case KEY_DOWN:
443 Decrement (widget->current, widget->inc);
444 break;
445
446 case KEY_UP:
447 Increment (widget->current, widget->inc);
448 break;
449
450 case KEY_PPAGE:
451 Increment (widget->current, widget->fastinc);
452 break;
453
454 case KEY_NPAGE:
455 Decrement (widget->current, widget->fastinc);
456 break;
457
458 case KEY_HOME:
459 widget->current = widget->low;
460 break;
461
462 case KEY_END:
463 widget->current = widget->high;
464 break;
465
466 case KEY_TAB:
467 case KEY_ENTER:
468 setExitType (widget, input);
469 ret = (widget->current);
470 complete = TRUE;
471 break;
472
473 case KEY_ESC:
474 setExitType (widget, input);
475 complete = TRUE;
476 break;
477
478 case KEY_ERROR:
479 setExitType (widget, input);
480 complete = TRUE;
481 break;
482
483 case CDK_REFRESH:
484 eraseCDKScreen (ScreenOf (widget));
485 refreshCDKScreen (ScreenOf (widget));
486 break;
487
488 default:
489 if (widget->fieldEdit)
490 {
491 if (!performEdit (widget, input))
492 Beep ();
493 }
494 else
495 {
496 /*
497 * The cursor is not within the editable text. Interpret
498 * input as commands.
499 */
500 switch (input)
501 {
502 case 'd':
503 case '-':
504 return _injectCDK<MIXED> (object, KEY_DOWN);
505 case '+':
506 return _injectCDK<MIXED> (object, KEY_UP);
507 case 'D':
508 return _injectCDK<MIXED> (object, KEY_NPAGE);
509 case '0':
510 return _injectCDK<MIXED> (object, KEY_HOME);
511 default:
512 Beep ();
513 break;
514 }
515 }
516 break;
517 }
518 }
519 limitCurrentValue (widget);
520
521 /* Should we call a post-process? */
522 if (!complete && (PostProcessFuncOf (widget) != 0))
523 {
524 PostProcessFuncOf (widget) (v<UPPER>,
525 widget,
526 PostProcessDataOf (widget),
527 input);
528 }
529 }
530
531 if (!complete)
532 {
533 drawCDK<MIXED>Field (widget);
534 setExitType (widget, 0);
535 }
536
537 ResultOf (widget).value<DTYPE> = ret;
538 return (ret != unknown<DTYPE>);
539 }
540
541 /*
542 * This moves the widget's data field to the given location.
543 */
544 static void _moveCDK<MIXED> (CDKOBJS *object,
545 int xplace,
546 int yplace,
547 boolean relative,
548 boolean refresh_flag)
549 {
550 CDK<UPPER> *widget = (CDK<UPPER> *) object;
551 int currentX = getbegx (widget->win);
552 int currentY = getbegy (widget->win);
553 int xpos = xplace;
554 int ypos = yplace;
555 int xdiff = 0;
556 int ydiff = 0;
557
558 /*
559 * If this is a relative move, then we will adjust where we want
560 * to move to.
561 */
562 if (relative)
563 {
564 xpos = getbegx (widget->win) + xplace;
565 ypos = getbegy (widget->win) + yplace;
566 }
567
568 /* Adjust the window if we need to. */
569 alignxy (WindowOf (widget), &xpos, &ypos, widget->boxWidth, widget->boxHeight);
570
571 /* Get the difference. */
572 xdiff = currentX - xpos;
573 ydiff = currentY - ypos;
574
575 /* Move the window to the new location. */
576 moveCursesWindow (widget->win, -xdiff, -ydiff);
577 moveCursesWindow (widget->labelWin, -xdiff, -ydiff);
578 moveCursesWindow (widget->fieldWin, -xdiff, -ydiff);
579 moveCursesWindow (widget->shadowWin, -xdiff, -ydiff);
580
581 /* Touch the windows so they 'move'. */
582 refreshCDKWindow (WindowOf (widget));
583
584 /* Redraw the window, if they asked for it. */
585 if (refresh_flag)
586 {
587 drawCDK<MIXED> (widget, ObjOf (widget)->box);
588 }
589 }
590
591 /*
592 * This function draws the widget.
593 */
594 static void _drawCDK<MIXED> (CDKOBJS *object, boolean Box)
595 {
596 CDK<UPPER> *widget = (CDK<UPPER> *) object;
597
598 /* Draw the shadow. */
599 if (widget->shadowWin != 0)
600 {
601 drawShadow (widget->shadowWin);
602 }
603
604 /* Box the widget if asked. */
605 if (Box)
606 {
607 drawObjBox (widget->win, ObjOf (widget));
608 }
609
610 drawCdkTitle (widget->win, object);
611
612 /* Draw the label. */
613 if (widget->labelWin != 0)
614 {
615 writeChtype (widget->labelWin, 0, 0,
616 widget->label,
617 HORIZONTAL, 0,
618 widget->labelLen);
619 wrefresh (widget->labelWin);
620 }
621 wrefresh (widget->win);
622
623 /* Draw the field window. */
624 drawCDK<MIXED>Field (widget);
625 }
626
627 /*
628 * This draws the widget.
629 */
Field(CDK<UPPER> * widget)630 static void drawCDK<MIXED>Field (CDK<UPPER> * widget)
631 {
632 char temp[256];
633
634 werase (widget->fieldWin);
635
636 /* Draw the value in the field. */
637 #if <FLOAT>
638 {
639 char format[256];
640 int digits = MINIMUM (widget->digits, 30);
641 sprintf (format, "%%.%i<PRINT>", digits);
642 sprintf (temp, format, widget->current);
643 }
644 #endif <FLOAT>
645 #if <INT>
646 sprintf (temp, "%<PRINT>", widget->current);
647 #endif <INT>
648 writeCharAttrib (widget->fieldWin,
649 widget->fieldWidth - (int)strlen (temp) - 1,
650 0,
651 temp,
652 widget->fieldAttr,
653 HORIZONTAL,
654 0,
655 (int)strlen (temp));
656
657 moveToEditPosition (widget, widget->fieldEdit);
658 wrefresh (widget->fieldWin);
659 }
660
661 /*
662 * This sets the background attribute of the widget.
663 */
664 static void _setBKattr<MIXED> (CDKOBJS *object, chtype attrib)
665 {
666 if (object != 0)
667 {
668 CDK<UPPER> *widget = (CDK<UPPER> *) object;
669
670 wbkgd (widget->win, attrib);
671 wbkgd (widget->fieldWin, attrib);
672 if (widget->labelWin != 0)
673 {
674 wbkgd (widget->labelWin, attrib);
675 }
676 }
677 }
678
679 /*
680 * This function destroys the widget.
681 */
682 static void _destroyCDK<MIXED> (CDKOBJS *object)
683 {
684 if (object != 0)
685 {
686 CDK<UPPER> *widget = (CDK<UPPER> *) object;
687
688 cleanCdkTitle (object);
689 freeChtype (widget->label);
690
691 /* Clean up the windows. */
692 deleteCursesWindow (widget->fieldWin);
693 deleteCursesWindow (widget->labelWin);
694 deleteCursesWindow (widget->shadowWin);
695 deleteCursesWindow (widget->win);
696
697 /* Clean the key bindings. */
698 cleanCDKObjectBindings (v<UPPER>, widget);
699
700 /* Unregister this object. */
701 unregisterCDKObject (v<UPPER>, widget);
702 }
703 }
704
705 /*
706 * This function erases the widget from the screen.
707 */
708 static void _eraseCDK<MIXED> (CDKOBJS *object)
709 {
710 if (validCDKObject (object))
711 {
712 CDK<UPPER> *widget = (CDK<UPPER> *) object;
713
714 eraseCursesWindow (widget->labelWin);
715 eraseCursesWindow (widget->fieldWin);
716 eraseCursesWindow (widget->win);
717 eraseCursesWindow (widget->shadowWin);
718 }
719 }
720
721 /*
722 * This function sets the low/high/current values of the widget.
723 */
724 void setCDK<MIXED> (CDK<UPPER> * widget, <CTYPE> low, <CTYPE> high, <CTYPE> value, boolean Box)
725 {
726 setCDK<MIXED>LowHigh (widget, low, high);
727 setCDK<MIXED>Value (widget, value);
728 setCDK<MIXED>Box (widget, Box);
729 }
730
731 /*
732 * This sets the digits.
733 */
734 #if <FLOAT>
Digits(CDK<UPPER> * widget,int digits)735 void setCDK<MIXED>Digits (CDK<UPPER> * widget, int digits)
736 {
737 widget->digits = MAXIMUM (0, digits);
738 }
739
Digits(CDK<UPPER> * widget)740 int getCDK<MIXED>Digits (CDK<UPPER> * widget)
741 {
742 return widget->digits;
743 }
744 #endif <FLOAT>
745
746 /*
747 * This sets the widget's value.
748 */
749 void setCDK<MIXED>Value (CDK<UPPER> * widget, <CTYPE> value)
750 {
751 widget->current = value;
752 limitCurrentValue (widget);
753 }
Value(CDK<UPPER> * widget)754 <CTYPE> getCDK<MIXED>Value (CDK<UPPER> * widget)
755 {
756 return widget->current;
757 }
758
759 /*
760 * This function sets the low/high values of the widget.
761 */
762 void setCDK<MIXED>LowHigh (CDK<UPPER> * widget, <CTYPE> low, <CTYPE> high)
763 {
764 /* Make sure the values aren't out of bounds. */
765 if (low <= high)
766 {
767 widget->low = low;
768 widget->high = high;
769 }
770 else
771 {
772 widget->low = high;
773 widget->high = low;
774 }
775
776 /* Make sure the user hasn't done something silly. */
777 limitCurrentValue (widget);
778 }
LowValue(CDK<UPPER> * widget)779 <CTYPE> getCDK<MIXED>LowValue (CDK<UPPER> * widget)
780 {
781 return widget->low;
782 }
HighValue(CDK<UPPER> * widget)783 <CTYPE> getCDK<MIXED>HighValue (CDK<UPPER> * widget)
784 {
785 return widget->high;
786 }
787
788 /*
789 * This sets the widget's box attribute.
790 */
Box(CDK<UPPER> * widget,boolean Box)791 void setCDK<MIXED>Box (CDK<UPPER> * widget, boolean Box)
792 {
793 ObjOf (widget)->box = Box;
794 ObjOf (widget)->borderSize = Box ? 1 : 0;
795 }
Box(CDK<UPPER> * widget)796 boolean getCDK<MIXED>Box (CDK<UPPER> * widget)
797 {
798 return ObjOf (widget)->box;
799 }
800
801 static void _focusCDK<MIXED> (CDKOBJS *object)
802 {
803 CDK<UPPER> *widget = (CDK<UPPER> *) object;
804
805 drawCDK<MIXED> (widget, ObjOf (widget)->box);
806 }
807
808 static void _unfocusCDK<MIXED> (CDKOBJS *object)
809 {
810 CDK<UPPER> *widget = (CDK<UPPER> *) object;
811
812 drawCDK<MIXED> (widget, ObjOf (widget)->box);
813 }
814
815 dummyRefreshData (<MIXED>)
816
817 dummySaveData (<MIXED>)
818