1 #include <cdk_int.h>
2 #include <scroller.h>
3 
4 /*
5  * $Author: tom $
6  * $Date: 2019/02/20 13:22:09 $
7  * $Revision: 1.164 $
8  */
9 
10 /*
11  * Declare file local prototypes.
12  */
13 static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box);
14 static int createCDKScrollItemList (CDKSCROLL *scrollp, boolean numbers,
15 				    CDK_CSTRING2 list, int listSize);
16 static void fixCursorPosition (CDKSCROLL *widget);
17 static void setViewSize (CDKSCROLL *scrollp, int listSize);
18 static int maxViewSize (CDKSCROLL *scrollp);
19 
20 #define NUMBER_FMT      "%4d. %s"
21 #define NUMBER_LEN(s)   (8 + strlen (s))
22 
23 /* Determine how many characters we can shift to the right */
24 /* before all the items have been scrolled off the screen. */
25 #define AvailableWidth(w)  ((w)->boxWidth - 2 * BorderOf (w))
26 #define updateViewWidth(w, widest) \
27 	(w)->maxLeftChar = (((w)->boxWidth > widest) \
28 			      ? 0 \
29 			      : (widest - AvailableWidth (w)))
30 #define WidestItem(w)      ((w)->maxLeftChar + AvailableWidth (w))
31 
32 #define SCREENPOS(w,n) (w)->itemPos[n] - (w)->leftChar	/* + scrollbarAdj + BorderOf(w) */
33 
34 DeclareCDKObjects (SCROLL, Scroll, setCdk, Int);
35 
36 /*
37  * This function creates a new scrolling list widget.
38  */
newCDKScroll(CDKSCREEN * cdkscreen,int xplace,int yplace,int splace,int height,int width,const char * title,CDK_CSTRING2 list,int listSize,boolean numbers,chtype highlight,boolean Box,boolean shadow)39 CDKSCROLL *newCDKScroll (CDKSCREEN *cdkscreen,
40 			 int xplace,
41 			 int yplace,
42 			 int splace,
43 			 int height,
44 			 int width,
45 			 const char *title,
46 			 CDK_CSTRING2 list,
47 			 int listSize,
48 			 boolean numbers,
49 			 chtype highlight,
50 			 boolean Box,
51 			 boolean shadow)
52 {
53    /* *INDENT-EQLS* */
54    CDKSCROLL *scrollp           = 0;
55    int parentWidth              = getmaxx (cdkscreen->window);
56    int parentHeight             = getmaxy (cdkscreen->window);
57    int boxWidth;
58    int boxHeight;
59    int xpos                     = xplace;
60    int ypos                     = yplace;
61    int scrollAdjust             = 0;
62    int x;
63    /* *INDENT-OFF* */
64    static const struct { int from; int to; } bindings[] = {
65 		{ CDK_BACKCHAR,	KEY_PPAGE },
66 		{ CDK_FORCHAR,	KEY_NPAGE },
67 		{ 'g',		KEY_HOME },
68 		{ '1',		KEY_HOME },
69 		{ 'G',		KEY_END },
70 		{ '<',		KEY_HOME },
71 		{ '>',		KEY_END },
72    };
73    /* *INDENT-ON* */
74 
75    if ((scrollp = newCDKObject (CDKSCROLL, &my_funcs)) == 0)
76    {
77       destroyCDKObject (scrollp);
78       return (0);
79    }
80 
81    setCDKScrollBox (scrollp, Box);
82 
83    /*
84     * If the height is a negative value, the height will
85     * be ROWS-height, otherwise, the height will be the
86     * given height.
87     */
88    boxHeight = setWidgetDimension (parentHeight, height, 0);
89 
90    /*
91     * If the width is a negative value, the width will
92     * be COLS-width, otherwise, the width will be the
93     * given width.
94     */
95    boxWidth = setWidgetDimension (parentWidth, width, 0);
96 
97    boxWidth = setCdkTitle (ObjOf (scrollp), title, boxWidth);
98 
99    /* Set the box height. */
100    if (TitleLinesOf (scrollp) > boxHeight)
101    {
102       boxHeight = (TitleLinesOf (scrollp)
103 		   + MINIMUM (listSize, 8)
104 		   + 2 * BorderOf (scrollp));
105    }
106 
107    /* Adjust the box width if there is a scrollp bar. */
108    if ((splace == LEFT) || (splace == RIGHT))
109    {
110       scrollp->scrollbar = TRUE;
111       boxWidth += 1;
112    }
113    else
114    {
115       scrollp->scrollbar = FALSE;
116    }
117 
118    /*
119     * Make sure we didn't extend beyond the dimensions of the window.
120     */
121    scrollp->boxWidth = (boxWidth > parentWidth
122 			? (parentWidth - scrollAdjust)
123 			: boxWidth);
124    scrollp->boxHeight = (boxHeight > parentHeight
125 			 ? parentHeight
126 			 : boxHeight);
127 
128    setViewSize (scrollp, listSize);
129 
130    /* Rejustify the x and y positions if we need to. */
131    alignxy (cdkscreen->window, &xpos, &ypos, scrollp->boxWidth, scrollp->boxHeight);
132 
133    /* Make the scrolling window */
134    scrollp->win = newwin (scrollp->boxHeight, scrollp->boxWidth, ypos, xpos);
135 
136    /* Is the scrolling window null?? */
137    if (scrollp->win == 0)
138    {
139       destroyCDKObject (scrollp);
140       return (0);
141    }
142 
143    /* Turn the keypad on for the window. */
144    keypad (scrollp->win, TRUE);
145 
146    /* Create the scrollbar window. */
147    if (splace == RIGHT)
148    {
149       scrollp->scrollbarWin = subwin (scrollp->win,
150 				      maxViewSize (scrollp), 1,
151 				      SCREEN_YPOS (scrollp, ypos),
152 				      xpos + scrollp->boxWidth
153 				      - BorderOf (scrollp) - 1);
154    }
155    else if (splace == LEFT)
156    {
157       scrollp->scrollbarWin = subwin (scrollp->win,
158 				      maxViewSize (scrollp), 1,
159 				      SCREEN_YPOS (scrollp, ypos),
160 				      SCREEN_XPOS (scrollp, xpos));
161    }
162    else
163    {
164       scrollp->scrollbarWin = 0;
165    }
166 
167    /* create the list window */
168 
169    scrollp->listWin = subwin (scrollp->win,
170 			      maxViewSize (scrollp),
171 			      scrollp->boxWidth
172 			      - 2 * BorderOf (scrollp) - scrollAdjust,
173 			      SCREEN_YPOS (scrollp, ypos),
174 			      SCREEN_XPOS (scrollp, xpos)
175 			      + (splace == LEFT ? 1 : 0));
176 
177    /* *INDENT-EQLS* Set the rest of the variables */
178    ScreenOf (scrollp)           = cdkscreen;
179    scrollp->parent              = cdkscreen->window;
180    scrollp->shadowWin           = 0;
181    scrollp->scrollbarPlacement  = splace;
182    scrollp->maxLeftChar         = 0;
183    scrollp->leftChar            = 0;
184    scrollp->highlight           = highlight;
185    initExitType (scrollp);
186    ObjOf (scrollp)->acceptsFocus = TRUE;
187    ObjOf (scrollp)->inputWindow = scrollp->win;
188    scrollp->shadow              = shadow;
189 
190    setCDKScrollPosition (scrollp, 0);
191 
192    /* Create the scrolling list item list and needed variables. */
193    if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0)
194    {
195       destroyCDKObject (scrollp);
196       return (0);
197    }
198 
199    /* Do we need to create a shadow? */
200    if (shadow)
201    {
202       scrollp->shadowWin = newwin (scrollp->boxHeight,
203 				   boxWidth,
204 				   ypos + 1,
205 				   xpos + 1);
206    }
207 
208    /* Setup the key bindings. */
209    for (x = 0; x < (int)SIZEOF (bindings); ++x)
210       bindCDKObject (vSCROLL,
211 		     scrollp,
212 		     (chtype)bindings[x].from,
213 		     getcCDKBind,
214 		     (void *)(long)bindings[x].to);
215 
216    registerCDKObject (cdkscreen, vSCROLL, scrollp);
217 
218    /* Return the scrolling list */
219    return scrollp;
220 }
221 
222 /*
223  * Put the cursor on the currently-selected item's row.
224  */
fixCursorPosition(CDKSCROLL * widget)225 static void fixCursorPosition (CDKSCROLL *widget)
226 {
227    scroller_FixCursorPosition ((CDKSCROLLER *)widget);
228 }
229 
230 /*
231  * This actually does all the 'real' work of managing the scrolling list.
232  */
activateCDKScroll(CDKSCROLL * scrollp,chtype * actions)233 int activateCDKScroll (CDKSCROLL *scrollp, chtype *actions)
234 {
235    /* Draw the scrolling list */
236    drawCDKScroll (scrollp, ObjOf (scrollp)->box);
237 
238    if (actions == 0)
239    {
240       chtype input;
241       boolean functionKey;
242 
243       for (;;)
244       {
245 	 int ret;
246 
247 	 fixCursorPosition (scrollp);
248 	 input = (chtype)getchCDKObject (ObjOf (scrollp), &functionKey);
249 
250 	 /* Inject the character into the widget. */
251 	 ret = injectCDKScroll (scrollp, input);
252 	 if (scrollp->exitType != vEARLY_EXIT)
253 	 {
254 	    return ret;
255 	 }
256       }
257    }
258    else
259    {
260       int length = chlen (actions);
261       int i = 0;
262 
263       /* Inject each character one at a time. */
264       for (i = 0; i < length; i++)
265       {
266 	 int ret = injectCDKScroll (scrollp, actions[i]);
267 	 if (scrollp->exitType != vEARLY_EXIT)
268 	    return ret;
269       }
270    }
271 
272    /* Set the exit type for the widget and return. */
273    setExitType (scrollp, 0);
274    return -1;
275 }
276 
277 /*
278  * This injects a single character into the widget.
279  */
_injectCDKScroll(CDKOBJS * object,chtype input)280 static int _injectCDKScroll (CDKOBJS *object, chtype input)
281 {
282    CDKSCROLL *myself = (CDKSCROLL *)object;
283    CDKSCROLLER *widget = (CDKSCROLLER *)object;
284    int ppReturn = 1;
285    int ret = unknownInt;
286    bool complete = FALSE;
287 
288    /* Set the exit type for the widget. */
289    setExitType (widget, 0);
290 
291    /* Draw the scrolling list */
292    drawCDKScrollList (myself, ObjOf (widget)->box);
293 
294    /* Check if there is a pre-process function to be called. */
295    if (PreProcessFuncOf (widget) != 0)
296    {
297       /* Call the pre-process function. */
298       ppReturn = PreProcessFuncOf (widget) (vSCROLL,
299 					    widget,
300 					    PreProcessDataOf (widget),
301 					    input);
302    }
303 
304    /* Should we continue? */
305    if (ppReturn != 0)
306    {
307       /* Check for a predefined key binding. */
308       if (checkCDKObjectBind (vSCROLL, widget, input) != 0)
309       {
310 	 checkEarlyExit (widget);
311 	 complete = TRUE;
312       }
313       else
314       {
315 	 switch (input)
316 	 {
317 	 case KEY_UP:
318 	    scroller_KEY_UP (widget);
319 	    break;
320 
321 	 case KEY_DOWN:
322 	    scroller_KEY_DOWN (widget);
323 	    break;
324 
325 	 case KEY_RIGHT:
326 	    scroller_KEY_RIGHT (widget);
327 	    break;
328 
329 	 case KEY_LEFT:
330 	    scroller_KEY_LEFT (widget);
331 	    break;
332 
333 	 case KEY_PPAGE:
334 	    scroller_KEY_PPAGE (widget);
335 	    break;
336 
337 	 case KEY_NPAGE:
338 	    scroller_KEY_NPAGE (widget);
339 	    break;
340 
341 	 case KEY_HOME:
342 	    scroller_KEY_HOME (widget);
343 	    break;
344 
345 	 case KEY_END:
346 	    scroller_KEY_END (widget);
347 	    break;
348 
349 	 case '$':
350 	    widget->leftChar = widget->maxLeftChar;
351 	    break;
352 
353 	 case '|':
354 	    widget->leftChar = 0;
355 	    break;
356 
357 	 case KEY_ESC:
358 	    setExitType (widget, input);
359 	    complete = TRUE;
360 	    break;
361 
362 	 case KEY_ERROR:
363 	    setExitType (widget, input);
364 	    complete = TRUE;
365 	    break;
366 
367 	 case CDK_REFRESH:
368 	    eraseCDKScreen (ScreenOf (widget));
369 	    refreshCDKScreen (ScreenOf (widget));
370 	    break;
371 
372 	 case KEY_TAB:
373 	 case KEY_ENTER:
374 	    setExitType (widget, input);
375 	    ret = widget->currentItem;
376 	    complete = TRUE;
377 	    break;
378 
379 	 default:
380 	    break;
381 	 }
382       }
383 
384       /* Should we call a post-process? */
385       if (!complete && (PostProcessFuncOf (widget) != 0))
386       {
387 	 PostProcessFuncOf (widget) (vSCROLL,
388 				     widget,
389 				     PostProcessDataOf (widget),
390 				     input);
391       }
392    }
393 
394    if (!complete)
395    {
396       drawCDKScrollList (myself, ObjOf (widget)->box);
397       setExitType (widget, 0);
398    }
399 
400    fixCursorPosition (myself);
401    ResultOf (widget).valueInt = ret;
402    return (ret != unknownInt);
403 }
404 
405 /*
406  * This allows the user to accelerate to a position in the scrolling list.
407  */
setCDKScrollPosition(CDKSCROLL * scrollp,int item)408 void setCDKScrollPosition (CDKSCROLL *scrollp, int item)
409 {
410    scroller_SetPosition ((CDKSCROLLER *)scrollp, item);
411 }
412 
413 /* obsolete (because the name is inconsistent) */
getCDKScrollCurrent(CDKSCROLL * scrollp)414 int getCDKScrollCurrent (CDKSCROLL *scrollp)
415 {
416    return scrollp->currentItem;
417 }
418 
setCDKScrollCurrent(CDKSCROLL * scrollp,int item)419 void setCDKScrollCurrent (CDKSCROLL *scrollp, int item)
420 {
421    scroller_SetPosition ((CDKSCROLLER *)scrollp, item);
422 }
423 
424 /*
425  * Get/Set the current item number of the scroller.
426  */
getCDKScrollCurrentItem(CDKSCROLL * widget)427 int getCDKScrollCurrentItem (CDKSCROLL *widget)
428 {
429    return widget->currentItem;
430 }
431 
setCDKScrollCurrentItem(CDKSCROLL * widget,int item)432 void setCDKScrollCurrentItem (CDKSCROLL *widget, int item)
433 {
434    scroller_SetPosition ((CDKSCROLLER *)widget, item);
435 }
436 
437 /*
438  * Get/Set the top line of the scroller.
439  */
getCDKScrollCurrentTop(CDKSCROLL * widget)440 int getCDKScrollCurrentTop (CDKSCROLL *widget)
441 {
442    return widget->currentTop;
443 }
444 
setCDKScrollCurrentTop(CDKSCROLL * widget,int item)445 void setCDKScrollCurrentTop (CDKSCROLL *widget, int item)
446 {
447    if (item < 0)
448       item = 0;
449    else if (item > widget->maxTopItem)
450       item = widget->maxTopItem;
451    widget->currentTop = item;
452 
453    scroller_SetPosition ((CDKSCROLLER *)widget, item);
454 }
455 
456 /*
457  * This moves the scroll field to the given location.
458  */
_moveCDKScroll(CDKOBJS * object,int xplace,int yplace,boolean relative,boolean refresh_flag)459 static void _moveCDKScroll (CDKOBJS *object,
460 			    int xplace,
461 			    int yplace,
462 			    boolean relative,
463 			    boolean refresh_flag)
464 {
465    /* *INDENT-EQLS* */
466    CDKSCROLL *scrollp = (CDKSCROLL *)object;
467    int currentX       = getbegx (scrollp->win);
468    int currentY       = getbegy (scrollp->win);
469    int xpos           = xplace;
470    int ypos           = yplace;
471    int xdiff          = 0;
472    int ydiff          = 0;
473 
474    /*
475     * If this is a relative move, then we will adjust where we want
476     * to move to.
477     */
478    if (relative)
479    {
480       xpos = getbegx (scrollp->win) + xplace;
481       ypos = getbegy (scrollp->win) + yplace;
482    }
483 
484    /* Adjust the window if we need to. */
485    alignxy (WindowOf (scrollp), &xpos, &ypos, scrollp->boxWidth, scrollp->boxHeight);
486 
487    /* Get the difference. */
488    xdiff = currentX - xpos;
489    ydiff = currentY - ypos;
490 
491    /* Move the window to the new location. */
492    moveCursesWindow (scrollp->win, -xdiff, -ydiff);
493    moveCursesWindow (scrollp->listWin, -xdiff, -ydiff);
494    moveCursesWindow (scrollp->shadowWin, -xdiff, -ydiff);
495    moveCursesWindow (scrollp->scrollbarWin, -xdiff, -ydiff);
496 
497    /* Touch the windows so they 'move'. */
498    refreshCDKWindow (WindowOf (scrollp));
499 
500    /* Redraw the window, if they asked for it. */
501    if (refresh_flag)
502    {
503       drawCDKScroll (scrollp, ObjOf (scrollp)->box);
504    }
505 }
506 
507 /*
508  * This function draws the scrolling list widget.
509  */
_drawCDKScroll(CDKOBJS * object,boolean Box)510 static void _drawCDKScroll (CDKOBJS *object, boolean Box)
511 {
512    CDKSCROLL *scrollp = (CDKSCROLL *)object;
513 
514    /* Draw in the shadow if we need to. */
515    if (scrollp->shadowWin != 0)
516       drawShadow (scrollp->shadowWin);
517 
518    drawCdkTitle (scrollp->win, object);
519 
520    /* Draw in the scolling list items. */
521    drawCDKScrollList (scrollp, Box);
522 }
523 
drawCDKScrollCurrent(CDKSCROLL * s)524 static void drawCDKScrollCurrent (CDKSCROLL *s)
525 {
526    if ((s->listSize > 0) && (s->listSize > s->currentItem))
527    {
528       /* Rehighlight the current menu item. */
529       int screenPos = s->itemPos[s->currentItem] - s->leftChar;
530       chtype highlight = HasFocusObj (s) ? s->highlight : A_NORMAL;
531 
532       writeChtypeAttrib (s->listWin,
533 			 (screenPos >= 0) ? screenPos : 0,
534 			 s->currentHigh,
535 			 s->item[s->currentItem],
536 			 highlight,
537 			 HORIZONTAL,
538 			 (screenPos >= 0) ? 0 : (1 - screenPos),
539 			 s->itemLen[s->currentItem]);
540    }
541 }
542 
maxViewSize(CDKSCROLL * scrollp)543 static int maxViewSize (CDKSCROLL *scrollp)
544 {
545    return scroller_MaxViewSize ((CDKSCROLLER *)scrollp);
546 }
547 
548 /*
549  * Set variables that depend upon the list-size.
550  */
setViewSize(CDKSCROLL * scrollp,int listSize)551 static void setViewSize (CDKSCROLL *scrollp, int listSize)
552 {
553    scroller_SetViewSize ((CDKSCROLLER *)scrollp, listSize);
554 }
555 
556 #undef  SCREEN_YPOS		/* because listWin is separate */
557 #define SCREEN_YPOS(w,n) (n)
558 
559 /*
560  * This redraws the scrolling list.
561  */
drawCDKScrollList(CDKSCROLL * scrollp,boolean Box)562 static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box)
563 {
564    /* If the list is empty, don't draw anything. */
565    if (scrollp->listSize > 0)
566    {
567       int j;
568 
569       /* Redraw the list */
570       for (j = 0; j < scrollp->viewSize; j++)
571       {
572 	 int k;
573 	 int xpos = SCREEN_YPOS (scrollp, 0);
574 	 int ypos = SCREEN_YPOS (scrollp, j);
575 
576 	 writeBlanks (scrollp->listWin, xpos, ypos,
577 		      HORIZONTAL, 0, scrollp->boxWidth - 2 * BorderOf (scrollp));
578 
579 	 k = j + scrollp->currentTop;
580 
581 	 /* Draw the elements in the scroll list. */
582 	 if (k < scrollp->listSize)
583 	 {
584 	    int screenPos = SCREENPOS (scrollp, k);
585 
586 	    /* Write in the correct line. */
587 	    writeChtype (scrollp->listWin,
588 			 (screenPos >= 0) ? screenPos : 1,
589 			 ypos,
590 			 scrollp->item[k],
591 			 HORIZONTAL,
592 			 (screenPos >= 0) ? 0 : (1 - screenPos),
593 			 scrollp->itemLen[k]);
594 	 }
595       }
596 
597       drawCDKScrollCurrent (scrollp);
598 
599       /* Determine where the toggle is supposed to be. */
600       if (scrollp->scrollbarWin != 0)
601       {
602 	 scrollp->togglePos = floorCDK (scrollp->currentItem * (double)scrollp->step);
603 
604 	 /* Make sure the toggle button doesn't go out of bounds. */
605 
606 	 if (scrollp->togglePos >= getmaxy (scrollp->scrollbarWin))
607 	    scrollp->togglePos = getmaxy (scrollp->scrollbarWin) - 1;
608 
609 	 /* Draw the scrollbar. */
610 	 (void)mvwvline (scrollp->scrollbarWin,
611 			 0, 0,
612 			 ACS_CKBOARD,
613 			 getmaxy (scrollp->scrollbarWin));
614 	 (void)mvwvline (scrollp->scrollbarWin,
615 			 scrollp->togglePos, 0,
616 			 ' ' | A_REVERSE,
617 			 scrollp->toggleSize);
618       }
619    }
620 
621    /* Box it if needed. */
622    if (Box)
623    {
624       drawObjBox (scrollp->win, ObjOf (scrollp));
625    }
626    else
627    {
628       touchwin (scrollp->win);
629    }
630    wrefresh (scrollp->win);
631 }
632 
633 /*
634  * This sets the background attribute of the widget.
635  */
_setBKattrScroll(CDKOBJS * object,chtype attrib)636 static void _setBKattrScroll (CDKOBJS *object, chtype attrib)
637 {
638    if (object != 0)
639    {
640       CDKSCROLL *widget = (CDKSCROLL *)object;
641 
642       wbkgd (widget->win, attrib);
643       wbkgd (widget->listWin, attrib);
644       if (widget->scrollbarWin != 0)
645       {
646 	 wbkgd (widget->scrollbarWin, attrib);
647       }
648    }
649 }
650 
651 /*
652  * This function destroys
653  */
_destroyCDKScroll(CDKOBJS * object)654 static void _destroyCDKScroll (CDKOBJS *object)
655 {
656    if (object != 0)
657    {
658       CDKSCROLL *scrollp = (CDKSCROLL *)object;
659 
660       cleanCdkTitle (object);
661       CDKfreeChtypes (scrollp->item);
662       freeChecked (scrollp->itemPos);
663       freeChecked (scrollp->itemLen);
664 
665       /* Clean up the windows. */
666       deleteCursesWindow (scrollp->scrollbarWin);
667       deleteCursesWindow (scrollp->shadowWin);
668       deleteCursesWindow (scrollp->listWin);
669       deleteCursesWindow (scrollp->win);
670 
671       /* Clean the key bindings. */
672       cleanCDKObjectBindings (vSCROLL, scrollp);
673 
674       /* Unregister this object. */
675       unregisterCDKObject (vSCROLL, scrollp);
676    }
677 }
678 
679 /*
680  * This function erases the scrolling list from the screen.
681  */
_eraseCDKScroll(CDKOBJS * object)682 static void _eraseCDKScroll (CDKOBJS *object)
683 {
684    if (validCDKObject (object))
685    {
686       CDKSCROLL *scrollp = (CDKSCROLL *)object;
687 
688       eraseCursesWindow (scrollp->win);
689       eraseCursesWindow (scrollp->shadowWin);
690    }
691 }
692 
allocListArrays(CDKSCROLL * scrollp,int newSize,bool first)693 static boolean allocListArrays (CDKSCROLL *scrollp,
694 				int newSize,
695 				bool first)
696 {
697    /* *INDENT-EQLS* */
698    boolean result;
699    chtype **newList     = 0;
700    int *newLen          = 0;
701    int *newPos          = 0;
702    int n;
703 
704    if (newSize != 0)
705    {
706       /* *INDENT-EQLS* */
707       int nchunk  = ((newSize + 1) | 31) + 1;
708       newList     = typeCallocN (chtype *, nchunk);
709       newLen      = typeCallocN (int, nchunk);
710       newPos      = typeCallocN (int, nchunk);
711    }
712 
713    /* if called via setCDKScrollItems(), free the obsolete data */
714    if ((first || (newSize == 0)) && scrollp->item && scrollp->listSize)
715    {
716       for (n = 0; n < scrollp->listSize; ++n)
717       {
718 	 freeAndNull (scrollp->item[n]);
719 	 scrollp->itemLen[n] = 0;
720 	 scrollp->itemPos[n] = 0;
721       }
722    }
723 
724    if (newList != 0 &&
725        newLen != 0 &&
726        newPos != 0)
727    {
728       if (!first)
729       {
730 	 for (n = 0; n < scrollp->listSize; ++n)
731 	 {
732 	    /* *INDENT-EQLS* */
733 	    newList[n] = scrollp->item[n];
734 	    newLen[n]  = scrollp->itemLen[n];
735 	    newPos[n]  = scrollp->itemPos[n];
736 	 }
737       }
738 
739       freeChecked (scrollp->item);
740       freeChecked (scrollp->itemPos);
741       freeChecked (scrollp->itemLen);
742 
743       /* *INDENT-EQLS* */
744       scrollp->listSize = newSize;
745       scrollp->item     = newList;
746       scrollp->itemLen  = newLen;
747       scrollp->itemPos  = newPos;
748 
749       result = TRUE;
750    }
751    else
752    {
753       freeAndNull (scrollp->item);
754       freeAndNull (scrollp->itemPos);
755       freeAndNull (scrollp->itemLen);
756 
757       freeChecked (newList);
758       freeChecked (newLen);
759       freeChecked (newPos);
760       result = FALSE;
761    }
762    return result;
763 }
764 
allocListItem(CDKSCROLL * scrollp,int which,char ** work,size_t * used,int number,const char * value)765 static boolean allocListItem (CDKSCROLL *scrollp,
766 			      int which,
767 			      char **work,
768 			      size_t * used,
769 			      int number,
770 			      const char *value)
771 {
772    if (number > 0)
773    {
774       size_t need = NUMBER_LEN (value);
775       if (need > *used)
776       {
777 	 *used = ((need + 2) * 2);
778 	 if (*work == 0)
779 	 {
780 	    if ((*work = (char *)malloc (*used)) == 0)
781 	       return FALSE;
782 	 }
783 	 else
784 	 {
785 	    if ((*work = (char *)realloc (*work, *used)) == 0)
786 	       return FALSE;
787 	 }
788       }
789       sprintf (*work, NUMBER_FMT, number, value);
790       value = *work;
791    }
792 
793    if ((scrollp->item[which] = char2Chtype (value,
794 					    &(scrollp->itemLen[which]),
795 					    &(scrollp->itemPos[which]))) == 0)
796       return FALSE;
797 
798    scrollp->itemPos[which] = justifyString (scrollp->boxWidth,
799 					    scrollp->itemLen[which],
800 					    scrollp->itemPos[which]);
801 
802    return TRUE;
803 }
804 
805 /*
806  * This function creates the scrolling list information and sets up the needed
807  * variables for the scrolling list to work correctly.
808  */
createCDKScrollItemList(CDKSCROLL * scrollp,boolean numbers,CDK_CSTRING2 list,int listSize)809 static int createCDKScrollItemList (CDKSCROLL *scrollp,
810 				    boolean numbers,
811 				    CDK_CSTRING2 list,
812 				    int listSize)
813 {
814    int status = 0;
815 
816    if (listSize > 0)
817    {
818       /* *INDENT-EQLS* */
819       size_t have               = 0;
820       char *temp                = 0;
821 
822       if (allocListArrays (scrollp, listSize, TRUE))
823       {
824 	 int widestItem = 0;
825 	 int x = 0;
826 
827 	 /* Create the items in the scrolling list. */
828 	 status = 1;
829 	 for (x = 0; x < listSize; x++)
830 	 {
831 	    if (!allocListItem (scrollp,
832 				x,
833 				&temp,
834 				&have,
835 				numbers ? (x + 1) : 0,
836 				list[x]))
837 	    {
838 	       status = 0;
839 	       break;
840 	    }
841 
842 	    widestItem = MAXIMUM (scrollp->itemLen[x], widestItem);
843 	 }
844 	 freeChecked (temp);
845 
846 	 if (status)
847 	 {
848 	    updateViewWidth (scrollp, widestItem);
849 
850 	    /* Keep the boolean flag 'numbers' */
851 	    scrollp->numbers = numbers;
852 	 }
853       }
854    }
855    else
856    {
857       status = 1;		/* null list is ok - for a while */
858    }
859 
860    return status;
861 }
862 
863 /*
864  * This sets certain attributes of the scrolling list.
865  */
setCDKScroll(CDKSCROLL * scrollp,CDK_CSTRING2 list,int listSize,boolean numbers,chtype highlight,boolean Box)866 void setCDKScroll (CDKSCROLL *scrollp,
867 		   CDK_CSTRING2 list,
868 		   int listSize,
869 		   boolean numbers,
870 		   chtype highlight,
871 		   boolean Box)
872 {
873    setCDKScrollItems (scrollp, list, listSize, numbers);
874    setCDKScrollHighlight (scrollp, highlight);
875    setCDKScrollBox (scrollp, Box);
876 }
877 
878 /*
879  * This sets the scrolling list items.
880  */
setCDKScrollItems(CDKSCROLL * scrollp,CDK_CSTRING2 list,int listSize,boolean numbers)881 void setCDKScrollItems (CDKSCROLL *scrollp, CDK_CSTRING2 list, int listSize, boolean numbers)
882 {
883    int x = 0;
884 
885    if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0)
886       return;
887 
888    /* Clean up the display. */
889    for (x = 0; x < scrollp->viewSize; x++)
890    {
891       writeBlanks (scrollp->win, 1, SCREEN_YPOS (scrollp, x),
892 		   HORIZONTAL, 0, scrollp->boxWidth - 2);
893    }
894 
895    setViewSize (scrollp, listSize);
896    setCDKScrollPosition (scrollp, 0);
897    scrollp->leftChar = 0;
898 }
getCDKScrollItems(CDKSCROLL * scrollp,char ** list)899 int getCDKScrollItems (CDKSCROLL *scrollp, char **list)
900 {
901    if (list != 0)
902    {
903       int x;
904 
905       for (x = 0; x < scrollp->listSize; x++)
906       {
907 	 list[x] = chtype2Char (scrollp->item[x]);
908       }
909    }
910    return scrollp->listSize;
911 }
912 
913 /*
914  * This sets the highlight of the scrolling list.
915  */
setCDKScrollHighlight(CDKSCROLL * scrollp,chtype highlight)916 void setCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight)
917 {
918    scrollp->highlight = highlight;
919 }
getCDKScrollHighlight(CDKSCROLL * scrollp,chtype highlight GCC_UNUSED)920 chtype getCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight GCC_UNUSED)
921 {
922    return scrollp->highlight;
923 }
924 
925 /*
926  * This sets the box attribute of the scrolling list.
927  */
setCDKScrollBox(CDKSCROLL * scrollp,boolean Box)928 void setCDKScrollBox (CDKSCROLL *scrollp, boolean Box)
929 {
930    ObjOf (scrollp)->box = Box;
931    ObjOf (scrollp)->borderSize = Box ? 1 : 0;
932 }
getCDKScrollBox(CDKSCROLL * scrollp)933 boolean getCDKScrollBox (CDKSCROLL *scrollp)
934 {
935    return ObjOf (scrollp)->box;
936 }
937 
938 /*
939  * Resequence the numbers after a insertion/deletion.
940  */
resequence(CDKSCROLL * scrollp)941 static void resequence (CDKSCROLL *scrollp)
942 {
943    if (scrollp->numbers)
944    {
945       int j, k;
946       for (j = 0; j < scrollp->listSize; ++j)
947       {
948 	 char source[80];
949 	 chtype *target = scrollp->item[j];
950 
951 	 sprintf (source, NUMBER_FMT, j + 1, "");
952 
953 	 for (k = 0; source[k] != 0; ++k)
954 	 {
955 	    /* handle deletions that change the length of number */
956 	    if (source[k] == '.' && CharOf (target[k]) != '.')
957 	    {
958 	       int k2 = k;
959 	       while ((target[k2] = target[k2 + 1]) != 0)
960 		  ++k2;
961 	       scrollp->itemLen[j] -= 1;
962 	    }
963 	    target[k] &= A_ATTRIBUTES;
964 	    target[k] |= (chtype)(unsigned char)source[k];
965 	 }
966       }
967    }
968 }
969 
insertListItem(CDKSCROLL * scrollp,int item)970 static boolean insertListItem (CDKSCROLL *scrollp, int item)
971 {
972    int x;
973    for (x = scrollp->listSize; x > item; --x)
974    {
975       scrollp->item[x] = scrollp->item[x - 1];
976       scrollp->itemLen[x] = scrollp->itemLen[x - 1];
977       scrollp->itemPos[x] = scrollp->itemPos[x - 1];
978    }
979    return TRUE;
980 }
981 
982 /*
983  * This adds a single item to a scrolling list, at the end of the list.
984  */
addCDKScrollItem(CDKSCROLL * scrollp,const char * item)985 void addCDKScrollItem (CDKSCROLL *scrollp, const char *item)
986 {
987    int itemNumber = scrollp->listSize;
988    int widestItem = WidestItem (scrollp);
989    char *temp = 0;
990    size_t have = 0;
991 
992    if (allocListArrays (scrollp, scrollp->listSize + 1, FALSE) &&
993        allocListItem (scrollp,
994 		      itemNumber,
995 		      &temp,
996 		      &have,
997 		      scrollp->numbers ? (itemNumber + 1) : 0,
998 		      item))
999    {
1000       /* Determine the size of the widest item. */
1001       widestItem = MAXIMUM (scrollp->itemLen[itemNumber], widestItem);
1002 
1003       updateViewWidth (scrollp, widestItem);
1004 
1005       setViewSize (scrollp, scrollp->listSize + 1);
1006    }
1007 
1008    freeChecked (temp);
1009 }
1010 
1011 /*
1012  * This adds a single item to a scrolling list, before the current item.
1013  */
insertCDKScrollItem(CDKSCROLL * scrollp,const char * item)1014 void insertCDKScrollItem (CDKSCROLL *scrollp, const char *item)
1015 {
1016    int widestItem = WidestItem (scrollp);
1017    char *temp = 0;
1018    size_t have = 0;
1019 
1020    if (allocListArrays (scrollp, scrollp->listSize + 1, FALSE) &&
1021        insertListItem (scrollp, scrollp->currentItem) &&
1022        allocListItem (scrollp,
1023 		      scrollp->currentItem,
1024 		      &temp,
1025 		      &have,
1026 		      scrollp->numbers ? (scrollp->currentItem + 1) : 0,
1027 		      item))
1028    {
1029       /* Determine the size of the widest item. */
1030       widestItem = MAXIMUM (scrollp->itemLen[scrollp->currentItem], widestItem);
1031 
1032       updateViewWidth (scrollp, widestItem);
1033 
1034       setViewSize (scrollp, scrollp->listSize + 1);
1035 
1036       resequence (scrollp);
1037    }
1038 
1039    freeChecked (temp);
1040 }
1041 
1042 /*
1043  * This removes a single item from a scrolling list.
1044  */
deleteCDKScrollItem(CDKSCROLL * scrollp,int position)1045 void deleteCDKScrollItem (CDKSCROLL *scrollp, int position)
1046 {
1047    if (position >= 0 && position < scrollp->listSize)
1048    {
1049       int x;
1050 
1051       freeChtype (scrollp->item[position]);
1052 
1053       /* Adjust the list. */
1054       for (x = position; x < scrollp->listSize; x++)
1055       {
1056 	 scrollp->item[x] = scrollp->item[x + 1];
1057 	 scrollp->itemLen[x] = scrollp->itemLen[x + 1];
1058 	 scrollp->itemPos[x] = scrollp->itemPos[x + 1];
1059       }
1060       setViewSize (scrollp, scrollp->listSize - 1);
1061 
1062       if (scrollp->listSize > 0)
1063 	 resequence (scrollp);
1064 
1065       if (scrollp->listSize < maxViewSize (scrollp))
1066 	 werase (scrollp->win);	/* force the next redraw to be complete */
1067 
1068       /* do this to update the view size, etc. */
1069       setCDKScrollPosition (scrollp, scrollp->currentItem);
1070    }
1071 }
1072 
_focusCDKScroll(CDKOBJS * object)1073 static void _focusCDKScroll (CDKOBJS *object)
1074 {
1075    CDKSCROLL *scrollp = (CDKSCROLL *)object;
1076 
1077    drawCDKScrollCurrent (scrollp);
1078    wrefresh (scrollp->listWin);
1079 }
1080 
_unfocusCDKScroll(CDKOBJS * object)1081 static void _unfocusCDKScroll (CDKOBJS *object)
1082 {
1083    CDKSCROLL *scrollp = (CDKSCROLL *)object;
1084 
1085    drawCDKScrollCurrent (scrollp);
1086    wrefresh (scrollp->listWin);
1087 }
1088 
1089 dummyRefreshData (Scroll)
1090 
1091 dummySaveData (Scroll)
1092