#include /* * $Author: Thorsten.Glaser $ * $Date: 2005/04/24 20:27:19 $ * $Revision: 1.138 $ */ /* * Declare file local prototypes. */ static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box); static int createCDKScrollItemList (CDKSCROLL *scrollp, boolean numbers, char **list, int listSize); static void setViewSize(CDKSCROLL *scrollp, int listSize); static int maxViewSize(CDKSCROLL *scrollp); #define NUMBER_FMT "%4d. %s" #define NUMBER_LEN(s) (8 + strlen(s)) /* Determine how many characters we can shift to the right */ /* before all the items have been scrolled off the screen. */ #define AvailableWidth(w) ((w)->boxWidth - 2*BorderOf(w)) #define updateViewWidth(w, widest) \ (w)->maxLeftChar = (((w)->boxWidth > widest) \ ? 0 \ : (widest - AvailableWidth(w))) #define WidestItem(w) ((w)->maxLeftChar + AvailableWidth(w)) DeclareCDKObjects(SCROLL, Scroll, setCdk, Int); /* * This function creates a new scrolling list widget. */ CDKSCROLL *newCDKScroll (CDKSCREEN *cdkscreen, int xplace, int yplace, int splace, int height, int width, char *title, char **list, int listSize, boolean numbers, chtype highlight, boolean Box, boolean shadow) { CDKSCROLL *scrollp = 0; int parentWidth = getmaxx(cdkscreen->window); int parentHeight = getmaxy(cdkscreen->window); int boxWidth = width; int boxHeight = height; int xpos = xplace; int ypos = yplace; int scrollAdjust = 0; int x; static const struct { int from; int to; } bindings[] = { { CDK_BACKCHAR, KEY_PPAGE }, { CDK_FORCHAR, KEY_NPAGE }, { 'g', KEY_HOME }, { '1', KEY_HOME }, { 'G', KEY_END }, { '<', KEY_HOME }, { '>', KEY_END }, }; if (listSize <= 0 || (scrollp = newCDKObject(CDKSCROLL, &my_funcs)) == 0) { destroyCDKObject(scrollp); return (0); } setCDKScrollBox (scrollp, Box); /* * If the height is a negative value, the height will * be ROWS-height, otherwise, the height will be the * given height. */ boxHeight = setWidgetDimension (parentHeight, height, 0); /* * If the width is a negative value, the width will * be COLS-width, otherwise, the width will be the * given width. */ boxWidth = setWidgetDimension (parentWidth, width, 0); boxWidth = setCdkTitle(ObjOf(scrollp), title, boxWidth); /* Set the box height. */ if (TitleLinesOf(scrollp) > boxHeight) { boxHeight = TitleLinesOf(scrollp) + MINIMUM(listSize, 8) + 2 * BorderOf(scrollp); } /* Adjust the box width if there is a scrollp bar. */ if ((splace == LEFT) || (splace == RIGHT)) { scrollp->scrollbar = TRUE; boxWidth += 1; } else { scrollp->scrollbar = FALSE; } /* * Make sure we didn't extend beyond the dimensions of the window. */ scrollp->boxWidth = (boxWidth > parentWidth ? (parentWidth - scrollAdjust) : boxWidth); scrollp->boxHeight = (boxHeight > parentHeight ? parentHeight : boxHeight); setViewSize(scrollp, listSize); /* Rejustify the x and y positions if we need to. */ alignxy (cdkscreen->window, &xpos, &ypos, scrollp->boxWidth, scrollp->boxHeight); /* Make the scrolling window */ scrollp->win = newwin (scrollp->boxHeight, scrollp->boxWidth, ypos, xpos); /* Is the scrolling window null?? */ if (scrollp->win == 0) { destroyCDKObject(scrollp); return (0); } /* Turn the keypad on for the window. */ keypad (scrollp->win, TRUE); /* Create the scrollbar window. */ if (splace == RIGHT) { scrollp->scrollbarWin = subwin (scrollp->win, maxViewSize(scrollp), 1, SCREEN_YPOS(scrollp, ypos), xpos + scrollp->boxWidth - BorderOf(scrollp) - 1); } else if (splace == LEFT) { scrollp->scrollbarWin = subwin (scrollp->win, maxViewSize(scrollp), 1, SCREEN_YPOS(scrollp, ypos), SCREEN_XPOS(scrollp, xpos)); } else { scrollp->scrollbarWin = 0; } /* create the list window */ scrollp->listWin = subwin(scrollp->win, maxViewSize(scrollp), scrollp->boxWidth - 2*BorderOf(scrollp) - scrollAdjust, SCREEN_YPOS(scrollp, ypos), SCREEN_XPOS(scrollp, xpos) + (splace == LEFT ? 1 : 0)); /* Set the rest of the variables */ ScreenOf(scrollp) = cdkscreen; scrollp->parent = cdkscreen->window; scrollp->shadowWin = 0; scrollp->scrollbarPlacement = splace; scrollp->maxLeftChar = 0; scrollp->leftChar = 0; scrollp->highlight = highlight; initExitType(scrollp); ObjOf(scrollp)->acceptsFocus = TRUE; ObjOf(scrollp)->inputWindow = scrollp->win; scrollp->shadow = shadow; setCDKScrollPosition(scrollp, 0); /* Create the scrolling list item list and needed variables. */ if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0) { destroyCDKObject(scrollp); return (0); } /* Do we need to create a shadow? */ if (shadow) { scrollp->shadowWin = newwin (scrollp->boxHeight, boxWidth, ypos + 1, xpos + 1); } /* Setup the key bindings. */ for (x = 0; x < (int) SIZEOF(bindings); ++x) bindCDKObject (vSCROLL, scrollp, bindings[x].from, getcCDKBind, (void *)(long)bindings[x].to); /* Register this baby. */ registerCDKObject (cdkscreen, vSCROLL, scrollp); /* Return the scrolling list */ return scrollp; } /* * This actually does all the 'real' work of managing the scrolling list. */ int activateCDKScroll (CDKSCROLL *scrollp, chtype *actions) { /* Draw the scrolling list */ drawCDKScroll (scrollp, ObjOf(scrollp)->box); /* Check if actions is null. */ if (actions == 0) { chtype input; int ret; for (;;) { /* Get the input. */ input = getcCDKObject (ObjOf(scrollp)); /* Inject the character into the widget. */ ret = injectCDKScroll (scrollp, input); if (scrollp->exitType != vEARLY_EXIT) { return ret; } } } else { int length = chlen (actions); int i = 0; int ret; /* Inject each character one at a time. */ for (i=0; i < length; i++) { ret = injectCDKScroll (scrollp, actions[i]); if (scrollp->exitType != vEARLY_EXIT) return ret; } } /* Set the exit type for the widget and return. */ setExitType(scrollp, 0); return -1; } /* * This injects a single character into the widget. */ static int _injectCDKScroll (CDKOBJS *object, chtype input) { CDKSCROLL *scrollp = (CDKSCROLL *)object; int ppReturn = 1; int ret = unknownInt; bool complete = FALSE; /* Set the exit type for the widget. */ setExitType(scrollp, 0); /* Draw the scrolling list */ drawCDKScrollList (scrollp, ObjOf(scrollp)->box); /* Check if there is a pre-process function to be called. */ if (PreProcessFuncOf(scrollp) != 0) { /* Call the pre-process function. */ ppReturn = PreProcessFuncOf(scrollp) (vSCROLL, scrollp, PreProcessDataOf(scrollp), input); } /* Should we continue? */ if (ppReturn != 0) { /* Check for a predefined key binding. */ if (checkCDKObjectBind (vSCROLL, scrollp, input) != 0) { checkEarlyExit(scrollp); complete = TRUE; } else { switch (input) { case KEY_UP : scroller_KEY_UP(scrollp); break; case KEY_DOWN : scroller_KEY_DOWN(scrollp); break; case KEY_RIGHT : scroller_KEY_RIGHT(scrollp); break; case KEY_LEFT : scroller_KEY_LEFT(scrollp); break; case KEY_PPAGE : scroller_KEY_PPAGE(scrollp); break; case KEY_NPAGE : scroller_KEY_NPAGE(scrollp); break; case KEY_HOME : scroller_KEY_HOME(scrollp); break; case KEY_END : scroller_KEY_END(scrollp); break; case '$' : scrollp->leftChar = scrollp->maxLeftChar; break; case '|' : scrollp->leftChar = 0; break; case KEY_ESC : setExitType(scrollp, input); complete = TRUE; break; case CDK_REFRESH : eraseCDKScreen (ScreenOf(scrollp)); refreshCDKScreen (ScreenOf(scrollp)); break; case KEY_TAB : case KEY_ENTER : setExitType(scrollp, input); ret = scrollp->currentItem; complete = TRUE; break; default : break; } } /* Should we call a post-process? */ if (!complete && (PostProcessFuncOf(scrollp) != 0)) { PostProcessFuncOf(scrollp) (vSCROLL, scrollp, PostProcessDataOf(scrollp), input); } } if (!complete) { drawCDKScrollList (scrollp, ObjOf(scrollp)->box); setExitType(scrollp, 0); } ResultOf(scrollp).valueInt = ret; return (ret != unknownInt); } /* * This allows the user to accelerate to a position in the scrolling list. */ void setCDKScrollPosition (CDKSCROLL *scrollp, int item) { scroller_SetPosition(scrollp, item); } int getCDKScrollCurrent (CDKSCROLL *scrollp) { return scrollp->currentItem; } void setCDKScrollCurrent (CDKSCROLL *scrollp, int item) { scroller_SetPosition(scrollp, item); } /* * This moves the scroll field to the given location. */ static void _moveCDKScroll (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag) { CDKSCROLL *scrollp = (CDKSCROLL *)object; int currentX = getbegx(scrollp->win); int currentY = getbegy(scrollp->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(scrollp->win) + xplace; ypos = getbegy(scrollp->win) + yplace; } /* Adjust the window if we need to. */ alignxy (WindowOf(scrollp), &xpos, &ypos, scrollp->boxWidth, scrollp->boxHeight); /* Get the difference. */ xdiff = currentX - xpos; ydiff = currentY - ypos; /* Move the window to the new location. */ moveCursesWindow(scrollp->win, -xdiff, -ydiff); moveCursesWindow(scrollp->listWin, -xdiff, -ydiff); moveCursesWindow(scrollp->shadowWin, -xdiff, -ydiff); moveCursesWindow(scrollp->scrollbarWin, -xdiff, -ydiff); /* Touch the windows so they 'move'. */ refreshCDKWindow (WindowOf(scrollp)); /* Redraw the window, if they asked for it. */ if (refresh_flag) { drawCDKScroll (scrollp, ObjOf(scrollp)->box); } } /* * This function draws the scrolling list widget. */ static void _drawCDKScroll (CDKOBJS *object, boolean Box) { CDKSCROLL *scrollp = (CDKSCROLL *)object; /* Draw in the shadow if we need to. */ if (scrollp->shadowWin != 0) drawShadow (scrollp->shadowWin); drawCdkTitle (scrollp->win, object); /* Draw in the scolling list items. */ drawCDKScrollList (scrollp, Box); } static void drawCDKScrollCurrent(CDKSCROLL *s) { /* Rehighlight the current menu item. */ int screenPos = s->itemPos[s->currentItem] - s->leftChar; chtype highlight = HasFocusObj(s) ? s->highlight : A_NORMAL; writeChtypeAttrib (s->listWin, (screenPos >= 0) ? screenPos : 0, s->currentHigh, s->item[s->currentItem], highlight, HORIZONTAL, (screenPos >= 0) ? 0 : (1 - screenPos), s->itemLen[s->currentItem]); } static int maxViewSize(CDKSCROLL *scrollp) { return scroller_MaxViewSize(scrollp); } /* * Set variables that depend upon the list-size. */ static void setViewSize(CDKSCROLL *scrollp, int listSize) { scroller_SetViewSize(scrollp, listSize); } #define SCREENPOS(w,n) (w)->itemPos[n] - (w)->leftChar /* + scrollbarAdj + BorderOf(w) */ #undef SCREEN_YPOS /* because listWin is separate */ #define SCREEN_YPOS(w,n) (n) /* * This redraws the scrolling list. */ static void drawCDKScrollList (CDKSCROLL *scrollp, boolean Box) { int j; /* If the list is empty, don't draw anything. */ if (scrollp->listSize > 0) { /* Redraw the list */ for (j=0; j < scrollp->viewSize; j++) { int k = j + scrollp->currentTop; writeBlanks (scrollp->listWin, 0, j, HORIZONTAL, 0, scrollp->boxWidth - 2 * BorderOf(scrollp)); /* Draw the elements in the scrolling list. */ if (k < scrollp->listSize) { int screenPos = SCREENPOS(scrollp, j + scrollp->currentTop); int ypos = SCREEN_YPOS(scrollp, j); /* Write in the correct line. */ writeChtype (scrollp->listWin, (screenPos >= 0) ? screenPos : 1, ypos, scrollp->item[k], HORIZONTAL, (screenPos >= 0) ? 0 : (1 - screenPos), scrollp->itemLen[k]); } } drawCDKScrollCurrent(scrollp); /* Determine where the toggle is supposed to be. */ if (scrollp->scrollbarWin != 0) { scrollp->togglePos = floorCDK(scrollp->currentItem * scrollp->step); /* Make sure the toggle button doesn't go out of bounds. */ if (scrollp->togglePos >= getmaxy(scrollp->scrollbarWin)) scrollp->togglePos = getmaxy(scrollp->scrollbarWin) - 1; /* Draw the scrollbar. */ mvwvline (scrollp->scrollbarWin, 0, 0, ACS_CKBOARD, getmaxy(scrollp->scrollbarWin)); mvwvline (scrollp->scrollbarWin, scrollp->togglePos, 0, ' ' | A_REVERSE, scrollp->toggleSize); } } /* Box it if needed. */ if (Box) { drawObjBox (scrollp->win, ObjOf(scrollp)); } /* Refresh the window. */ refreshCDKWindow (scrollp->win); } /* * This sets the background attribute of the widget. */ static void _setBKattrScroll (CDKOBJS *object, chtype attrib) { if (object != 0) { CDKSCROLL *widget = (CDKSCROLL *) object; wbkgd (widget->win, attrib); wbkgd (widget->listWin, attrib); if (widget->scrollbarWin != 0) { wbkgd (widget->scrollbarWin, attrib); } } } /* * This function destroys */ static void _destroyCDKScroll (CDKOBJS *object) { if (object != 0) { CDKSCROLL *scrollp = (CDKSCROLL *)object; cleanCdkTitle (object); CDKfreeChtypes (scrollp->item); freeChecked (scrollp->itemPos); freeChecked (scrollp->itemLen); /* Clean up the windows. */ deleteCursesWindow (scrollp->scrollbarWin); deleteCursesWindow (scrollp->shadowWin); deleteCursesWindow (scrollp->listWin); deleteCursesWindow (scrollp->win); /* Unregister this object. */ unregisterCDKObject (vSCROLL, scrollp); } } /* * This function erases the scrolling list from the screen. */ static void _eraseCDKScroll (CDKOBJS *object) { if (validCDKObject (object)) { CDKSCROLL *scrollp = (CDKSCROLL *)object; eraseCursesWindow (scrollp->win); eraseCursesWindow (scrollp->shadowWin); } } static boolean allocListArrays(CDKSCROLL *scrollp, int oldSize, int newSize) { boolean result; int n; int nchunk = ((newSize + 1) | 31) + 1; chtype ** newList = typeCallocN(chtype *, nchunk); int * newLen = typeCallocN(int, nchunk); int * newPos = typeCallocN(int, nchunk); if (newList != 0 && newLen != 0 && newPos != 0) { for (n = 0; n < oldSize; ++n) { newList[n] = scrollp->item[n]; newLen[n] = scrollp->itemLen[n]; newPos[n] = scrollp->itemPos[n]; } if (oldSize == 0) { CDKfreeChtypes (scrollp->item); freeChecked (scrollp->itemPos); freeChecked (scrollp->itemLen); } scrollp->item = newList; scrollp->itemLen = newLen; scrollp->itemPos = newPos; result = TRUE; } else { freeChecked(newList); freeChecked(newLen); freeChecked(newPos); result = FALSE; } return result; } static boolean allocListItem(CDKSCROLL *scrollp, int which, char **work, unsigned *used, int number, char *value) { if (number > 0) { unsigned need = NUMBER_LEN(value); if (need > *used) { *used = ((need + 2) * 2); if (*work == 0) { if ((*work = (char *)malloc(*used)) == 0) return FALSE; } else { if ((*work = (char *)realloc(*work, *used)) == 0) return FALSE; } } sprintf (*work, NUMBER_FMT, number, value); value = *work; } if ((scrollp->item[which] = char2Chtype (value, &(scrollp->itemLen[which]), &(scrollp->itemPos[which]))) == 0) return FALSE; scrollp->itemPos[which] = justifyString (scrollp->boxWidth, scrollp->itemLen[which], scrollp->itemPos[which]); return TRUE; } /* * This function creates the scrolling list information and sets up the needed * variables for the scrolling list to work correctly. */ static int createCDKScrollItemList (CDKSCROLL *scrollp, boolean numbers, char **list, int listSize) { int status = 0; if (listSize > 0) { int widestItem = 0; int x = 0; unsigned have = 0; char *temp = 0; if (allocListArrays(scrollp, 0, listSize)) { /* Create the items in the scrolling list. */ status = 1; for (x=0 ; x < listSize; x++) { if (!allocListItem(scrollp, x, &temp, &have, numbers ? (x + 1) : 0, list[x])) { status = 0; break; } widestItem = MAXIMUM (scrollp->itemLen[x], widestItem); } freeChecked (temp); if (status) { updateViewWidth(scrollp, widestItem); /* Keep the boolean flag 'numbers' */ scrollp->numbers = numbers; } } } return status; } /* * This sets certain attributes of the scrolling list. */ void setCDKScroll (CDKSCROLL *scrollp, char **list, int listSize, boolean numbers, chtype highlight, boolean Box) { setCDKScrollItems (scrollp, list, listSize, numbers); setCDKScrollHighlight (scrollp, highlight); setCDKScrollBox (scrollp, Box); } /* * This sets the scrolling list items. */ void setCDKScrollItems (CDKSCROLL *scrollp, char **list, int listSize, boolean numbers) { int x = 0; if (createCDKScrollItemList (scrollp, numbers, list, listSize) <= 0) return; /* Clean up the display. */ for (x=0; x < scrollp->viewSize ; x++) { writeBlanks (scrollp->win, 1, SCREEN_YPOS(scrollp, x), HORIZONTAL, 0, scrollp->boxWidth-2); } setViewSize(scrollp, listSize); setCDKScrollPosition(scrollp, 0); scrollp->leftChar = 0; } int getCDKScrollItems (CDKSCROLL *scrollp, char *list[]) { int x; for (x=0; x < scrollp->listSize; x++) { list[x] = chtype2Char (scrollp->item[x]); } return scrollp->listSize; } /* * This sets the highlight of the scrolling list. */ void setCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight) { scrollp->highlight = highlight; } chtype getCDKScrollHighlight (CDKSCROLL *scrollp, chtype highlight GCC_UNUSED) { return scrollp->highlight; } /* * This sets the box attribute of the scrolling list. */ void setCDKScrollBox (CDKSCROLL *scrollp, boolean Box) { ObjOf(scrollp)->box = Box; ObjOf(scrollp)->borderSize = Box ? 1 : 0; } boolean getCDKScrollBox (CDKSCROLL *scrollp) { return ObjOf(scrollp)->box; } /* * Resequence the numbers after a insertion/deletion. */ static void resequence (CDKSCROLL *scrollp) { if (scrollp->numbers) { int j, k; for (j = 0; j < scrollp->listSize; ++j) { char source[80]; chtype *target = scrollp->item[j]; sprintf(source, NUMBER_FMT, j + 1, ""); for (k = 0; source[k] != 0; ++k) { /* handle deletions that change the length of number */ if (source[k] == '.' && CharOf(target[k]) != '.') { int k2 = k; while ((target[k2] = target[k2 + 1]) != 0) ++k2; scrollp->itemLen[j] -= 1; } target[k] &= A_ATTRIBUTES; target[k] |= source[k]; } } } } static boolean insertListItem (CDKSCROLL *scrollp, int item) { int x; for (x = scrollp->listSize; x > item; --x) { scrollp->item[x] = scrollp->item[x - 1]; scrollp->itemLen[x] = scrollp->itemLen[x - 1]; scrollp->itemPos[x] = scrollp->itemPos[x - 1]; } return TRUE; } /* * This adds a single item to a scrolling list, at the end of the list. */ void addCDKScrollItem (CDKSCROLL *scrollp, char *item) { int itemNumber = scrollp->listSize; int widestItem = WidestItem(scrollp); char *temp = 0; unsigned have = 0; if (allocListArrays(scrollp, scrollp->listSize, scrollp->listSize + 1) && allocListItem(scrollp, itemNumber, &temp, &have, scrollp->numbers ? (itemNumber + 1) : 0, item)) { /* Determine the size of the widest item. */ widestItem = MAXIMUM (scrollp->itemLen[itemNumber], widestItem); updateViewWidth(scrollp, widestItem); setViewSize(scrollp, scrollp->listSize + 1); } freeChecked(temp); } /* * This adds a single item to a scrolling list, before the current item. */ void insertCDKScrollItem (CDKSCROLL *scrollp, char *item) { int widestItem = WidestItem(scrollp); char *temp = 0; unsigned have = 0; if (allocListArrays(scrollp, scrollp->listSize, scrollp->listSize + 1) && insertListItem(scrollp, scrollp->currentItem) && allocListItem(scrollp, scrollp->currentItem, &temp, &have, scrollp->numbers ? (scrollp->currentItem + 1) : 0, item)) { /* Determine the size of the widest item. */ widestItem = MAXIMUM (scrollp->itemLen[scrollp->currentItem], widestItem); updateViewWidth(scrollp, widestItem); setViewSize(scrollp, scrollp->listSize + 1); resequence(scrollp); } freeChecked(temp); } /* * This removes a single item from a scrolling list. */ void deleteCDKScrollItem (CDKSCROLL *scrollp, int position) { int x; if (position >= 0 && position < scrollp->listSize) { freeChtype (scrollp->item[position]); /* Adjust the list. */ for (x=position; x < scrollp->listSize; x++) { scrollp->item[x] = scrollp->item[x + 1]; scrollp->itemLen[x] = scrollp->itemLen[x + 1]; scrollp->itemPos[x] = scrollp->itemPos[x + 1]; } setViewSize(scrollp, scrollp->listSize - 1); if (scrollp->listSize > 0) resequence(scrollp); if (scrollp->listSize < maxViewSize(scrollp)) werase(scrollp->win); /* force the next redraw to be complete */ /* do this to update the view size, etc. */ setCDKScrollPosition (scrollp, scrollp->currentItem); } } static void _focusCDKScroll(CDKOBJS *object) { CDKSCROLL *scrollp = (CDKSCROLL *)object; drawCDKScrollCurrent(scrollp); wrefresh (scrollp->listWin); } static void _unfocusCDKScroll(CDKOBJS *object) { CDKSCROLL *scrollp = (CDKSCROLL *)object; drawCDKScrollCurrent(scrollp); wrefresh (scrollp->listWin); } dummyRefreshData(Scroll) dummySaveData(Scroll)