#include /* * $Author: tom $ * $Date: 2006/05/04 10:42:58 $ * $Revision: 1.102 $ */ /* * Declare file local prototypes. */ static BINDFN_PROTO (adjustAlphalistCB); static BINDFN_PROTO (completeWordCB); static int preProcessEntryField (EObjectType cdktype, void *object, void *clientData, chtype input); static int createList (CDKALPHALIST *alphalist, char **list, int listSize); DeclareSetXXchar (static, _setMy); DeclareCDKObjects (ALPHALIST, Alphalist, _setMy, String); /* * This creates the alphalist widget. */ CDKALPHALIST *newCDKAlphalist (CDKSCREEN *cdkscreen, int xplace, int yplace, int height, int width, char *title, char *label, char **list, int listSize, chtype fillerChar, chtype highlight, boolean Box, boolean shadow) { CDKALPHALIST *alphalist = 0; chtype *chtypeLabel = 0; int parentWidth = getmaxx (cdkscreen->window); int parentHeight = getmaxy (cdkscreen->window); int boxWidth = width; int boxHeight = height; int xpos = xplace; int ypos = yplace; int tempWidth = 0; int tempHeight = 0; int labelLen = 0; int x, junk2; static const struct { int from; int to; } bindings[] = { { CDK_BACKCHAR, KEY_PPAGE }, { CDK_FORCHAR, KEY_NPAGE }, }; if ((alphalist = newCDKObject (CDKALPHALIST, &my_funcs)) == 0 || !createList (alphalist, list, listSize)) { destroyCDKObject (alphalist); return (0); } setCDKAlphalistBox (alphalist, 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); /* Translate the label char *pointer to a chtype pointer. */ if (label != 0) { chtypeLabel = char2Chtype (label, &labelLen, &junk2); freeChtype (chtypeLabel); } /* Rejustify the x and y positions if we need to. */ alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight); /* Make the file selector window. */ alphalist->win = newwin (boxHeight, boxWidth, ypos, xpos); if (alphalist->win == 0) { destroyCDKObject (alphalist); return (0); } keypad (alphalist->win, TRUE); /* Set some variables. */ ScreenOf (alphalist) = cdkscreen; alphalist->parent = cdkscreen->window; alphalist->highlight = highlight; alphalist->fillerChar = fillerChar; alphalist->boxHeight = boxHeight; alphalist->boxWidth = boxWidth; initExitType (alphalist); alphalist->shadow = shadow; alphalist->shadowWin = 0; /* Do we want a shadow? */ if (shadow) { alphalist->shadowWin = newwin (boxHeight, boxWidth, ypos + 1, xpos + 1); } /* Create the entry field. */ tempWidth = (isFullWidth (width) ? FULL : boxWidth - 2 - labelLen); alphalist->entryField = newCDKEntry (cdkscreen, getbegx (alphalist->win), getbegy (alphalist->win), title, label, A_NORMAL, fillerChar, vMIXED, tempWidth, 0, 512, Box, FALSE); if (alphalist->entryField == 0) { destroyCDKObject (alphalist); return (0); } setCDKEntryLLChar (alphalist->entryField, ACS_LTEE); setCDKEntryLRChar (alphalist->entryField, ACS_RTEE); /* Set the key bindings for the entry field. */ bindCDKObject (vENTRY, alphalist->entryField, KEY_UP, adjustAlphalistCB, alphalist); bindCDKObject (vENTRY, alphalist->entryField, KEY_DOWN, adjustAlphalistCB, alphalist); bindCDKObject (vENTRY, alphalist->entryField, KEY_NPAGE, adjustAlphalistCB, alphalist); bindCDKObject (vENTRY, alphalist->entryField, KEY_PPAGE, adjustAlphalistCB, alphalist); bindCDKObject (vENTRY, alphalist->entryField, KEY_TAB, completeWordCB, alphalist); /* Set up the post-process function for the entry field. */ setCDKEntryPreProcess (alphalist->entryField, preProcessEntryField, alphalist); /* * Create the scrolling list. It overlaps the entry field by one line if * we are using box-borders. */ tempHeight = getmaxy (alphalist->entryField->win) - BorderOf (alphalist); tempWidth = (isFullWidth (width) ? FULL : boxWidth - 1); alphalist->scrollField = newCDKScroll (cdkscreen, getbegx (alphalist->win), getbegy (alphalist->entryField->win) + tempHeight, RIGHT, boxHeight - tempHeight, tempWidth, 0, list, listSize, NONUMBERS, A_REVERSE, Box, FALSE); setCDKScrollULChar (alphalist->scrollField, ACS_LTEE); setCDKScrollURChar (alphalist->scrollField, ACS_RTEE); /* Setup the key bindings. */ for (x = 0; x < (int) SIZEOF (bindings); ++x) bindCDKObject (vALPHALIST, alphalist, bindings[x].from, getcCDKBind, (void *)(long)bindings[x].to); registerCDKObject (cdkscreen, vALPHALIST, alphalist); return (alphalist); } /* * This erases the file selector from the screen. */ static void _eraseCDKAlphalist (CDKOBJS *object) { if (validCDKObject (object)) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; eraseCDKScroll (alphalist->scrollField); eraseCDKEntry (alphalist->entryField); eraseCursesWindow (alphalist->shadowWin); eraseCursesWindow (alphalist->win); } } /* * This moves the alphalist field to the given location. */ static void _moveCDKAlphalist (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; int currentX = getbegx (alphalist->win); int currentY = getbegy (alphalist->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 (alphalist->win) + xplace; ypos = getbegy (alphalist->win) + yplace; } /* Adjust the window if we need to. */ alignxy (WindowOf (alphalist), &xpos, &ypos, alphalist->boxWidth, alphalist->boxHeight); /* Get the difference. */ xdiff = currentX - xpos; ydiff = currentY - ypos; /* Move the window to the new location. */ moveCursesWindow (alphalist->win, -xdiff, -ydiff); moveCursesWindow (alphalist->shadowWin, -xdiff, -ydiff); /* Move the sub-widgets. */ moveCDKEntry (alphalist->entryField, xplace, yplace, relative, FALSE); moveCDKScroll (alphalist->scrollField, xplace, yplace, relative, FALSE); /* Touch the windows so they 'move'. */ refreshCDKWindow (WindowOf (alphalist)); /* Redraw the window, if they asked for it. */ if (refresh_flag) { drawCDKAlphalist (alphalist, ObjOf (alphalist)->box); } } /* * The alphalist's focus resides in the entry widget. But the scroll widget * will not draw items highlighted unless it has focus. Temporarily adjust the * focus of the scroll widget when drawing on it to get the right highlighting. */ #define SaveFocus(widget) \ boolean save = HasFocusObj (ObjOf (widget->scrollField)); \ HasFocusObj (ObjOf (widget->scrollField)) = \ HasFocusObj (ObjOf (widget->entryField)) #define RestoreFocus(widget) \ HasFocusObj (ObjOf (widget->scrollField)) = save static void drawMyScroller (CDKALPHALIST *widget) { SaveFocus (widget); drawCDKScroll (widget->scrollField, ObjOf (widget->scrollField)->box); RestoreFocus (widget); } static void injectMyScroller (CDKALPHALIST *widget, chtype key) { SaveFocus (widget); injectCDKScroll (widget->scrollField, key); RestoreFocus (widget); } /* * This draws the file selector widget. */ static void _drawCDKAlphalist (CDKOBJS *obj, boolean Box GCC_UNUSED) { CDKALPHALIST *alphalist = (CDKALPHALIST *)obj; /* Does this widget have a shadow? */ if (alphalist->shadowWin != 0) { drawShadow (alphalist->shadowWin); } /* Draw in the entry field. */ drawCDKEntry (alphalist->entryField, ObjOf (alphalist->entryField)->box); /* Draw in the scroll field. */ drawMyScroller (alphalist); } /* * This activates the file selector. */ char *activateCDKAlphalist (CDKALPHALIST *alphalist, chtype *actions) { char *ret = 0; /* Draw the widget. */ drawCDKAlphalist (alphalist, ObjOf (alphalist)->box); /* Activate the widget. */ ret = activateCDKEntry (alphalist->entryField, actions); /* Copy the exit type from the entry field. */ copyExitType (alphalist, alphalist->entryField); /* Determine the exit status. */ if (alphalist->exitType != vEARLY_EXIT) { return ret; } return 0; } /* * This injects a single character into the alphalist. */ static int _injectCDKAlphalist (CDKOBJS *object, chtype input) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; char *ret = unknownString; /* Draw the widget. */ drawCDKAlphalist (alphalist, ObjOf (alphalist)->box); /* Inject a character into the widget. */ ret = injectCDKEntry (alphalist->entryField, input); /* Copy the exit type from the entry field. */ copyExitType (alphalist, alphalist->entryField); /* Determine the exit status. */ if (alphalist->exitType == vEARLY_EXIT) ret = unknownString; ResultOf (alphalist).valueString = ret; return (ret != unknownString); } /* * This sets multiple attributes of the widget. */ void setCDKAlphalist (CDKALPHALIST *alphalist, char **list, int listSize, chtype fillerChar, chtype highlight, boolean Box) { setCDKAlphalistContents (alphalist, list, listSize); setCDKAlphalistFillerChar (alphalist, fillerChar); setCDKAlphalistHighlight (alphalist, highlight); setCDKAlphalistBox (alphalist, Box); } /* * This function sets the information inside the file selector. */ void setCDKAlphalistContents (CDKALPHALIST *widget, char **list, int listSize) { CDKSCROLL *scrollp = widget->scrollField; CDKENTRY *entry = widget->entryField; if (!createList (widget, list, listSize)) return; /* Set the information in the scrolling list. */ setCDKScroll (scrollp, widget->list, widget->listSize, NONUMBERS, scrollp->highlight, ObjOf (scrollp)->box); /* Clean out the entry field. */ setCDKAlphalistCurrentItem (widget, 0); cleanCDKEntry (entry); /* Redraw the widget. */ eraseCDKAlphalist (widget); drawCDKAlphalist (widget, ObjOf (widget)->box); } /* * This returns the contents of the widget. */ char **getCDKAlphalistContents (CDKALPHALIST *widget, int *size) { (*size) = widget->listSize; return widget->list; } /* * Get/set the current position in the scroll-widget. */ int getCDKAlphalistCurrentItem (CDKALPHALIST *widget) { return getCDKScrollCurrent (widget->scrollField); } void setCDKAlphalistCurrentItem (CDKALPHALIST *widget, int item) { if (widget->listSize != 0) { setCDKScrollCurrent (widget->scrollField, item); setCDKEntryValue (widget->entryField, widget->list[getCDKScrollCurrentItem(widget->scrollField)]); } } /* * This sets the filler character of the entry field of the alphalist. */ void setCDKAlphalistFillerChar (CDKALPHALIST *alphalist, chtype fillerCharacter) { CDKENTRY *entry = (CDKENTRY *)alphalist->entryField; alphalist->fillerChar = fillerCharacter; setCDKEntryFillerChar (entry, fillerCharacter); } chtype getCDKAlphalistFillerChar (CDKALPHALIST *alphalist) { return alphalist->fillerChar; } /* * This sets the highlight bar attributes. */ void setCDKAlphalistHighlight (CDKALPHALIST *alphalist, chtype highlight) { alphalist->highlight = highlight; } chtype getCDKAlphalistHighlight (CDKALPHALIST *alphalist) { return alphalist->highlight; } /* * This sets whether or not the widget will be drawn with a box. */ void setCDKAlphalistBox (CDKALPHALIST *alphalist, boolean Box) { ObjOf (alphalist)->box = Box; ObjOf (alphalist)->borderSize = Box ? 1 : 0; } boolean getCDKAlphalistBox (CDKALPHALIST *alphalist) { return ObjOf (alphalist)->box; } /* * These functions set the drawing characters of the widget. */ static void _setMyULchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKEntryULChar (alphalist->entryField, character); } static void _setMyURchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKEntryURChar (alphalist->entryField, character); } static void _setMyLLchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKScrollLLChar (alphalist->scrollField, character); } static void _setMyLRchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKScrollLRChar (alphalist->scrollField, character); } static void _setMyVTchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKEntryVerticalChar (alphalist->entryField, character); setCDKScrollVerticalChar (alphalist->scrollField, character); } static void _setMyHZchar (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKEntryHorizontalChar (alphalist->entryField, character); setCDKScrollHorizontalChar (alphalist->scrollField, character); } static void _setMyBXattr (CDKOBJS *object, chtype character) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; setCDKEntryBoxAttribute (alphalist->entryField, character); setCDKScrollBoxAttribute (alphalist->scrollField, character); } /* * This sets the background attribute of the widget. */ static void _setBKattrAlphalist (CDKOBJS *obj, chtype attrib) { CDKALPHALIST *alphalist = (CDKALPHALIST *)obj; setCDKEntryBackgroundAttrib (alphalist->entryField, attrib); setCDKScrollBackgroundAttrib (alphalist->scrollField, attrib); } static void destroyInfo (CDKALPHALIST *widget) { CDKfreeStrings (widget->list); widget->list = 0; widget->listSize = 0; } /* * This destroys the file selector. */ static void _destroyCDKAlphalist (CDKOBJS *object) { if (object != 0) { CDKALPHALIST *alphalist = (CDKALPHALIST *)object; destroyInfo (alphalist); /* Clean the key bindings. */ cleanCDKObjectBindings (vALPHALIST, alphalist); destroyCDKEntry (alphalist->entryField); destroyCDKScroll (alphalist->scrollField); /* Free up the window pointers. */ deleteCursesWindow (alphalist->shadowWin); deleteCursesWindow (alphalist->win); /* Unregister the object. */ unregisterCDKObject (vALPHALIST, alphalist); } } /* * This function sets the pre-process function. */ void setCDKAlphalistPreProcess (CDKALPHALIST *alphalist, PROCESSFN callback, void *data) { setCDKEntryPreProcess (alphalist->entryField, callback, data); } /* * This function sets the post-process function. */ void setCDKAlphalistPostProcess (CDKALPHALIST *alphalist, PROCESSFN callback, void *data) { setCDKEntryPostProcess (alphalist->entryField, callback, data); } /* * Start of callback functions. */ static int adjustAlphalistCB (EObjectType objectType GCC_UNUSED, void *object GCC_UNUSED, void *clientData, chtype key) { CDKALPHALIST *alphalist = (CDKALPHALIST *)clientData; CDKSCROLL *scrollp = alphalist->scrollField; CDKENTRY *entry = alphalist->entryField; char *current = 0; if (scrollp->listSize > 0) { /* Adjust the scrolling list. */ injectMyScroller (alphalist, key); /* Set the value in the entry field. */ current = chtype2Char (scrollp->item[scrollp->currentItem]); setCDKEntryValue (entry, current); drawCDKEntry (entry, ObjOf (entry)->box); freeChar (current); return (TRUE); } Beep (); return (FALSE); } /* * This is the heart-beat of the widget. */ static int preProcessEntryField (EObjectType cdktype GCC_UNUSED, void *object GCC_UNUSED, void *clientData, chtype input) { CDKALPHALIST *alphalist = (CDKALPHALIST *)clientData; CDKSCROLL *scrollp = alphalist->scrollField; CDKENTRY *entry = alphalist->entryField; int infoLen = ((entry->info != 0) ? (int)strlen (entry->info) : 0); int result = 1; bool empty = FALSE; /* Make sure the entry field isn't empty. */ if (entry->info == 0) { empty = TRUE; } else if (isCDKObjectBind (ObjTypeOf (alphalist), ObjOf (alphalist), input)) { result = 1; /* don't try to use this key in editing */ } else if ((isChar (input) && (isalnum (CharOf (input)) || ispunct (input))) || input == KEY_BACKSPACE || input == KEY_DC) { int Index, difference, absoluteDifference, x; int currPos = (entry->screenCol + entry->leftChar); char *pattern = malloc (infoLen + 2); if (pattern != 0) { strcpy (pattern, entry->info); if (input == KEY_BACKSPACE || input == KEY_DC) { if (input == KEY_BACKSPACE) --currPos; if (currPos >= 0) strcpy (pattern + currPos, entry->info + currPos + 1); } else { pattern[currPos] = (char)input; strcpy (pattern + currPos + 1, entry->info + currPos); } } if (pattern == 0) { Beep (); } else if (strlen (pattern) == 0) { empty = TRUE; } else if ((Index = searchList (alphalist->list, alphalist->listSize, pattern)) >= 0) { difference = Index - scrollp->currentItem; absoluteDifference = abs (difference); /* * If the difference is less than zero, then move up. * Otherwise move down. * * If the difference is greater than 10 jump to the new * index position. Otherwise provide the nice scroll. */ if (absoluteDifference <= 10) { for (x = 0; x < absoluteDifference; x++) { injectMyScroller (alphalist, ((difference <= 0) ? KEY_UP : KEY_DOWN)); } } else { setCDKScrollPosition (scrollp, Index); } drawMyScroller (alphalist); } else { Beep (); result = 0; } if (pattern != 0) free (pattern); } if (empty) { setCDKScrollPosition (scrollp, 0); drawMyScroller (alphalist); } return result; } /* * This tries to complete the word in the entry field. */ static int completeWordCB (EObjectType objectType GCC_UNUSED, void *object GCC_UNUSED, void *clientData, chtype key GCC_UNUSED) { CDKALPHALIST *alphalist = (CDKALPHALIST *)clientData; CDKENTRY *entry = (CDKENTRY*)alphalist->entryField; CDKSCROLL *scrollp = 0; int currentIndex = 0; int wordLength = 0; int selected = -1; int altCount = 0; int height = 0; int match = 0; int Index = 0; int ret = 0; int x = 0; char **altWords = 0; unsigned used = 0; if (entry->info == 0) { Beep (); return (TRUE); } wordLength = (int)strlen (entry->info); /* If the word length is equal to zero, just leave. */ if (wordLength == 0) { Beep (); return (TRUE); } /* Look for a unique word match. */ Index = searchList (alphalist->list, alphalist->listSize, entry->info); /* If the index is less than zero, return we didn't find a match. */ if (Index < 0) { Beep (); return (TRUE); } /* Did we find the last word in the list? */ if (Index == alphalist->listSize - 1) { setCDKEntryValue (entry, alphalist->list[Index]); drawCDKEntry (entry, ObjOf (entry)->box); return (TRUE); } /* Ok, we found a match, is the next item similar? */ ret = strncmp (alphalist->list[Index + 1], entry->info, wordLength); if (ret == 0) { currentIndex = Index; altCount = 0; height = 0; match = 0; selected = -1; /* Start looking for alternate words. */ /* FIXME: bsearch would be more suitable */ while ((currentIndex < alphalist->listSize) && (strncmp (alphalist->list[currentIndex], entry->info, wordLength) == 0)) { used = CDKallocStrings (&altWords, alphalist->list[currentIndex++], altCount++, used); } /* Determine the height of the scrolling list. */ height = (altCount < 8 ? altCount + 3 : 11); /* Create a scrolling list of close matches. */ scrollp = newCDKScroll (entry->obj.screen, CENTER, CENTER, RIGHT, height, -30, "Possible Matches.", altWords, altCount, NUMBERS, A_REVERSE, TRUE, FALSE); /* Allow them to select a close match. */ match = activateCDKScroll (scrollp, 0); selected = scrollp->currentItem; /* Check how they exited the list. */ if (scrollp->exitType == vESCAPE_HIT) { /* Destroy the scrolling list. */ destroyCDKScroll (scrollp); /* Clean up. */ CDKfreeStrings (altWords); /* Beep at the user. */ Beep (); /* Redraw the alphalist and return. */ drawCDKAlphalist (alphalist, ObjOf (alphalist)->box); return (TRUE); } /* Destroy the scrolling list. */ destroyCDKScroll (scrollp); /* Set the entry field to the selected value. */ setCDKEntry (entry, altWords[match], entry->min, entry->max, ObjOf (entry)->box); /* Move the highlight bar down to the selected value. */ for (x = 0; x < selected; x++) { injectMyScroller (alphalist, KEY_DOWN); } /* Clean up. */ CDKfreeStrings (altWords); /* Redraw the alphalist. */ drawCDKAlphalist (alphalist, ObjOf (alphalist)->box); } else { /* Set the entry field with the found item. */ setCDKEntry (entry, alphalist->list[Index], entry->min, entry->max, ObjOf (entry)->box); drawCDKEntry (entry, ObjOf (entry)->box); } return (TRUE); } static int createList (CDKALPHALIST *alphalist, char **list, int listSize) { int status = 0; if (listSize > 0) { char **newlist = typeCallocN (char *, listSize + 1); if (newlist != 0) { int x; /* * We'll sort the list before we use it. It would have been better to * declare list[] const and only modify the copy, but there may be * clients that rely on the old behavior. */ sortList (list, listSize); /* Copy in the new information. */ status = 1; for (x = 0; x < listSize; x++) { if ((newlist[x] = copyChar (list[x])) == 0) { status = 0; break; } } if (status) { destroyInfo (alphalist); alphalist->listSize = listSize; alphalist->list = newlist; } else { CDKfreeStrings (newlist); } } } else { destroyInfo (alphalist); status = TRUE; } return status; } static void _focusCDKAlphalist (CDKOBJS *object) { CDKALPHALIST *widget = (CDKALPHALIST *)object; FocusObj (ObjOf (widget->entryField)); } static void _unfocusCDKAlphalist (CDKOBJS *object) { CDKALPHALIST *widget = (CDKALPHALIST *)object; UnfocusObj (ObjOf (widget->entryField)); } dummyRefreshData (Alphalist) dummySaveData (Alphalist)