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