/*------------------------- DEMOLIST.C -------------------------*/ /* */ /* This file contains the VISIONS LIBRARY LIST DEMO software. */ /* */ /* Copyright 1990 Dan Vogel & David Bernazzani */ /* */ /* Date Initials Comments */ /* */ /* 03/07/90 DCV Initial Release 0.00 */ /*--------------------------------------------------------------*/ #include #include #include #include #include #include "USERCOMP.H" #include "USERLIST.H" #include "USERWIND.H" #include "USERFORM.H" #include "DEMOPROT.H" LOCAL TEXT *cnotice="VISIONS Copyright 1990 Dan Vogel & David Bernazzani"; /*--------------------------------------------------------------*/ /* Window Commands */ #define WIND_INIT 0 /* Create a window. */ #define WIND_UPDATE 1 /* Update the window displayed data. */ #define WIND_DEL 2 /* Close the window. */ /*-----------------------------------------------------------------*/ /* Run_List */ /* This is the list library demonstration code. */ /* */ /* The VISIONS list library demonstration code below is set up */ /* to allow the user to interactively create a displayed list */ /* of up to 10 string items. These items may be added, appended, */ /* deleted, searched, and sorted. Cursor highlights of the list */ /* items are controlled by the arrow keys, Ins, and Del. This */ /* allows the list functions to be invoked by the user with visual */ /* feedback. Implementing this mechanism on the screen requires */ /* the following visual 'areas': a command window, a list display */ /* window, a string entry form, a list length counter window, a */ /* response window and an introductory explanation window. */ /* Separate windows are set up for each of these functions, and */ /* then a keystroke driven execution loop is entered to allow the */ /* user interaction. */ /* More complex list examples are given in the comments of this */ /* code. */ /* WARNING - Most error checking has been removed to make the */ /* code easier to read. Users of VISIONS should check for error */ /* returns! */ /*-----------------------------------------------------------------*/ WORD Run_List() { WINDOW_HEAD *clear_window; /* Window to clear screen. */ WINDOW_HEAD *command_window; /* Commands available window. */ WINDOW_HEAD *list_window; /* List display. */ WINDOW_HEAD *length_window; /* List length display. */ WINDOW_HEAD *response_window; /* Error response screen. */ LIST_HEAD *demo_list; /* Demonstration list pointer. */ LIST_LINK *list_cursor; /* Screen list cursor pointer. */ LIST_LINK *tmp_cursor; /* Temporary cursor holder. */ UBYTE *item_pointer; /* List item pointer. */ WORD list_num; /* Number of items in the list. */ WORD key; /* User input holder. */ WORD status=0; /*-------------------------------------------*/ /* Create a window just to clear the screen. */ /*-------------------------------------------*/ Window_Define(&clear_window, 1, 1, 24, 80, TEXT_BLACK, TEXT_WHITE, "", POPUP_BIT); Window_Display(clear_window); /*-------------------------------------------*/ /* Tell the user what this routine is about. */ /*-------------------------------------------*/ List_Intro(); /*-------------------------------*/ /* Display the list of commands. */ /*-------------------------------*/ Command_Wind(&command_window, WIND_INIT); /*---------------------------------------------*/ /* Initialize the list and the display window. */ /*---------------------------------------------*/ List_Define(&demo_list,*CompareString,*ReleaseString,*CopyString); list_cursor = NULL; Display_Wind(&list_window, WIND_INIT, demo_list, list_cursor); /*------------------------------------*/ /* Initialize the list length window. */ /*------------------------------------*/ Length_Wind(&length_window, WIND_INIT, demo_list); /*-----------------------------------------*/ /* Initialize the command response window. */ /*-----------------------------------------*/ Response_Wind(&response_window, WIND_INIT, ""); /*-------------------------------------------------------*/ /* This is the user command interpreter loop. The */ /* basic structure is similar to most interpreters, */ /* a token input followed by a switch or dispatch */ /* command that actually handles the different commands. */ /*-------------------------------------------------------*/ while (((TEXT)(key = Comp_GetKey())) != ESC) /* Until user strikes ESC */ { /*---------------------------------*/ /* Clear the last response window. */ /*---------------------------------*/ Response_Wind(&response_window, WIND_UPDATE, ""); /*--------------------------*/ /* Interpret the keystroke. */ /*--------------------------*/ switch (key) { case HOME: /* Move cursor to top of displayed list. */ List_Top(demo_list, &list_cursor); break; case END: /* Move cursor to bottom of displayed list. */ List_Bottom(demo_list,&list_cursor); break; case UP: /* Move cursor up one position on list. */ List_Last(list_cursor,&tmp_cursor); if (tmp_cursor != NULL) list_cursor = tmp_cursor; break; case DOWN: /* Move cursor down one position on displayed list. */ List_Next(list_cursor,&tmp_cursor); if (tmp_cursor != NULL) list_cursor = tmp_cursor; break; case INS: /* Add a list item before the current cursor. */ if (List_Count(demo_list) >= 10) /* Maximum list length is 10. */ Response_Wind(&response_window, WIND_UPDATE, "List is already full."); else { if (EnterListItem(&item_pointer) != -1) { /* If user enters an item */ status = List_Add(demo_list, list_cursor, item_pointer); if (status != 0) /* Handle error here by deleting item! */ { free(item_pointer); /* Release storage and tell user. */ Response_Wind(&response_window, WIND_UPDATE, "Unable to insert new list item."); status = 0; } else /* If all went well, set cursor to new item. */ List_Find(demo_list, item_pointer, &list_cursor); } } break; case DEL: /* Delete the current list item. */ if (List_GetItem(list_cursor, &item_pointer) == 0) { /* If the cursor was good, set up new cursor, then delete. */ List_Next(list_cursor,&tmp_cursor); if (tmp_cursor != NULL) list_cursor = tmp_cursor; else List_Last(list_cursor,&list_cursor); if (List_DeleteItem(demo_list, item_pointer) != 0) Response_Wind(&response_window, WIND_UPDATE, "Unable to delete list item."); } else Response_Wind(&response_window, WIND_UPDATE, "Cursor does not point to valid list link."); break; case 'A': /* Append an item to the list. */ case 'a': if (List_Count(demo_list) >= 10) /* Check Max list length. */ Response_Wind(&response_window, WIND_UPDATE, "List is already full."); else { if (EnterListItem(&item_pointer) != -1) { /* If user enters an item */ status = List_Append(demo_list, item_pointer); if (status != 0) /* Handle error here by deleting item! */ { free(item_pointer); /* Release storage and tell user. */ Response_Wind(&response_window, WIND_UPDATE, "Unable to append new list item."); status = 0; } } List_Bottom(demo_list,&list_cursor); /* Set cursor to new item. */ } break; case 'S': /* Sort the list and redisplay it. */ case 's': List_Sort(demo_list,*CompareString); List_Top(demo_list, &list_cursor); /* Set cursor to list top. */ break; case 'J': /* Jump to the nth item in the list. */ case 'j': if (List_GetItemNumber(&list_num) >= 0) /* Get # to jump to. */ { if (list_num >= 0) { if (List_Nth(demo_list, list_num, &tmp_cursor) != 0) Response_Wind(&response_window, WIND_UPDATE, "Invalid list item number."); else list_cursor = tmp_cursor; } } break; } /*-----------------------------*/ /* Update the display windows. */ /*-----------------------------*/ Display_Wind(&list_window,WIND_UPDATE,demo_list,list_cursor); Length_Wind(&length_window, WIND_UPDATE, demo_list); } /*----------------------------------*/ /* Delete the list and the windows. */ /*----------------------------------*/ Command_Wind(&command_window, WIND_DEL); Display_Wind(&list_window,WIND_DEL,demo_list,list_cursor); Length_Wind(&length_window, WIND_DEL, demo_list); Response_Wind(&response_window, WIND_DEL, ""); List_Delete(demo_list); Window_Remove(clear_window); Window_Delete(clear_window); return(status); } /*--------------------------------------------------------*/ /* CopyString */ /* This is the simple list example version of the display */ /* item routine. Since the simple example keeps lists */ /* of character strings, this routine just copies the */ /* list character string over into an output buffer for */ /* display. No other formatting takes place. */ /* If the list were a structure, for example: */ /* struct employee_info */ /* { */ /* char first_name[10]; */ /* char last_name[10]; */ /* char ss_number[12]; */ /* } */ /* then this routine might look more like: */ /* WORD Format_Employee(instr,outstr) */ /* UBYTE *instr, *outstr; */ /* { */ /* struct employee_info *employee; */ /* */ /* employee = (struct employee_info *) instr; */ /* strcpy(outstr,employee->first_name); */ /* strcat(outstr," "); */ /* strcat(outstr,employee->last_name); */ /* strcat(outstr," "); */ /* strcat(outstr,employee->ss_number); */ /* return(0); */ /* } */ /* */ /* Note that you are not tied to a single display format,*/ /* this is just the routine used by DisplayList. You may */ /* create different display routines for uses at different*/ /* times, and call them directly instead of through the */ /* library. */ /*--------------------------------------------------------*/ WORD CopyString(instr, outstr) UBYTE *instr, *outstr; { strcpy(outstr,instr); return(0); } /*------------------------------------------------------------*/ /* CompareString */ /* This is the simple list example version of the item */ /* compare routine. Again, as the items here are simple */ /* character strings, we can use strcmp to do the comparison. */ /* A more complex case such as the example given in */ /* CopyString would require comparisons of all the items */ /* within the structure. */ /*------------------------------------------------------------*/ WORD CompareString(instr, outstr) UBYTE *instr, *outstr; { return(strcmp(instr,outstr)); } /*-------------------------------------------------------*/ /* ReleaseString */ /* This is the simple list example version of the item */ /* delete routine. Since we are using character strings */ /* allocated from the heap, we just 'free' them. If we */ /* were using predefined variables, like X="123", we */ /* would not need this routine at all! */ /* The CopyString example of the use of a structure */ /* would not change this routine. */ /*-------------------------------------------------------*/ WORD ReleaseString(delstr) UBYTE *delstr; { return(free(delstr)); } /*---------------------------------------------------*/ /* */ /* The remainder of the file consists primarily of */ /* screen handling routines. While these may be of */ /* interest as examples of forms and windows library */ /* use, they are not directly related to the list */ /* library demonstration. */ /* */ /*---------------------------------------------------*/ /*-----------------------------------------------*/ /* List_Intro */ /* Introduce the user to this demonstration, */ /* explaining the purpose and what is happening. */ /*-----------------------------------------------*/ WORD List_Intro() { WINDOW_HEAD *intro_window; Window_Define(&intro_window, 1, 1, 24, 80, TEXT_BLUE, TEXT_WHITE, "VISIONS List Demonstration Introduction", SINGLE_BORDER_BIT | POPUP_BIT); Window_Display(intro_window); Window_Mesg(9,7, " The VISIONS list library demonstration is intended to introduce "); Window_Mesg(10,7, " the user to the list library concepts and functions through the "); Window_Mesg(11,7, " use of a simple interactive example. This example takes the form "); Window_Mesg(12,7, " of an interactive manipulation of a list of up to ten string "); Window_Mesg(13,7, " elements. Each action taken by the user causes a list manipulation "); Window_Mesg(14,7, " to be performed by one or more VISIONS list library routines. This "); Window_Mesg(15,7, " is displayed in the list display window. The source code used to "); Window_Mesg(16,7, " create this demonstration is supplied with the executable. More "); Window_Mesg(17,7, " complex examples of list management are given in comments in the "); Window_Mesg(18,7," source code."); Window_Mesg(24,28,"Hit any key to continue."); Comp_GetAnyKey(); /* Force a key strike by the user. */ Window_Remove(intro_window); /* And destroy the window. */ Window_Delete(intro_window); return(0); } /*-------------------------------------------*/ /* Command_Wind */ /* Display the command set that the user has */ /* available for the interactive list demo. */ /* Use the passed window pointer for display.*/ /*-------------------------------------------*/ /* Command Window Parameters */ #define COMMAND_ROW 3 #define COMMAND_COL 36 #define COMMAND_WIDTH 43 #define COMMAND_HEIGHT 13 #define COMMAND_BKCOL TEXT_RED #define COMMAND_TXTCOL TEXT_BWHITE #define COMMAND_TITLE "List Editing Commands" #define COMMAND_BORDER SINGLE_BORDER_BIT WORD Command_Wind(command_window, cmd) WINDOW_HEAD **command_window; /* The window pointer to use. */ WORD cmd; /* The window command (INIT, DELete. */ { switch(cmd) { case WIND_INIT: Window_Define(command_window, COMMAND_ROW, COMMAND_COL, COMMAND_HEIGHT, COMMAND_WIDTH, COMMAND_BKCOL, COMMAND_TXTCOL, COMMAND_TITLE, COMMAND_BORDER | EXPLODE_BIT | IMPLODE_BIT | POPUP_BIT); Window_Display(*command_window); Window_Mesg(4,3,"HOME - Move cursor to list top"); Window_Mesg(5,3,"END - Move cursor to list bottom"); Window_Mesg(6,3,"UP - Move cursor up one item"); Window_Mesg(7,3,"DOWN - Move Cursor down one item"); Window_Mesg(8,3,"INS - Insert new item into list"); Window_Mesg(9,3,"DEL - Delete current item from list"); Window_Mesg(10,3,"A - Append new item to list"); Window_Mesg(11,3,"S - Sort list and redisplay"); Window_Mesg(12,3,"J - Move cursor to numbered list item"); break; case WIND_DEL: Window_Remove(*command_window); Window_Delete(*command_window); break; } return(0); } /*--------------------------------------------------------------*/ /* Display_Wind */ /* The following window is used to display the list contents. */ /* This could normally be done much more simply by calling the */ /* DisplayList routine. This would only display the list */ /* until a key was struck however. Therefore the routine */ /* below was written to leave the list display up on the screen */ /* during the entire demonstration of the list library. */ /*--------------------------------------------------------------*/ /* List Display Window Parameters */ #define DISPLAY_ROW 5 #define DISPLAY_COL 3 #define DISPLAY_WIDE 25 #define DISPLAY_HEIGHT 14 #define DISPLAY_BKCOL TEXT_BLUE #define DISPLAY_TXTCOL TEXT_WHITE #define DISPLAY_HIBKCOL TEXT_BLUE #define DISPLAY_HITXTCOL TEXT_BWHITE #define DISPLAY_TITLE "List Display" #define DISPLAY_BORDER DOUBLE_BORDER_BIT WORD Display_Wind(list_window, cmd, demo_list, list_cursor) WINDOW_HEAD **list_window; /* The window pointer to use. */ LIST_HEAD *demo_list; /* The list to display. */ LIST_LINK *list_cursor; /* The list item to highlight. */ WORD cmd; /* The window display command. */ { LIST_LINK *list_travel; TEXT buffer[81]; WORD disp_row=0; switch(cmd) { case WIND_INIT: /* Initially define and display the window. */ Window_Define(list_window, DISPLAY_ROW, DISPLAY_COL, DISPLAY_HEIGHT, DISPLAY_WIDE, DISPLAY_BKCOL, DISPLAY_TXTCOL, DISPLAY_TITLE, DISPLAY_BORDER | EXPLODE_BIT | IMPLODE_BIT | POPUP_BIT); Window_Display(*list_window); case WIND_UPDATE: /* Redisplay the window, with cursor color inverted.*/ Window_SetPtr(*list_window); List_Top(demo_list, &list_travel); while (disp_row++ < 10) /* Display the list of up to 10 items. */ { if (list_travel == NULL) strcpy(buffer," "); else List_DisplayItem(demo_list, list_travel, buffer); if (list_travel == list_cursor) /* Highlighted item? */ { Comp_SetBkColor(DISPLAY_HIBKCOL); Comp_SetTextColor(DISPLAY_HITXTCOL); } while (strlen(buffer) < DISPLAY_WIDE - 4) strcat(buffer," "); Window_Mesg((UBYTE)(disp_row + 3), (UBYTE)3, buffer); if (list_travel == list_cursor) /* Reset colors after highlight. */ { Comp_SetBkColor(DISPLAY_BKCOL); Comp_SetTextColor(DISPLAY_TXTCOL); } List_Next(list_travel,&list_travel); } break; case WIND_DEL: /* Finally shut down the window. */ Window_Remove(*list_window); Window_Delete(*list_window); break; } return(0); } /*---------------------------------------------*/ /* Length_Wind */ /* This creates a small window used to display */ /* the number of items in the current list. */ /*---------------------------------------------*/ /* List Length Window Parameters */ #define LENGTH_ROW 3 #define LENGTH_COL 3 #define LENGTH_WIDTH 25 #define LENGTH_HEIGHT 2 #define LENGTH_BKCOL TEXT_GREEN #define LENGTH_TXTCOL TEXT_BWHITE #define LENGTH_TITLE "Number of Items in List" #define LENGTH_BORDER 0 WORD Length_Wind(length_window, cmd, demo_list) WINDOW_HEAD **length_window; /* The window pointer to use. */ LIST_HEAD *demo_list; /* The list to count. */ WORD cmd; /* The window command. */ { WORD length; TEXT buffer[20]; switch(cmd) { case WIND_INIT: /* Define and display the window. */ Window_Define(length_window, LENGTH_ROW, LENGTH_COL, LENGTH_HEIGHT, LENGTH_WIDTH, LENGTH_BKCOL, LENGTH_TXTCOL, LENGTH_TITLE, LENGTH_BORDER | EXPLODE_BIT | IMPLODE_BIT | POPUP_BIT); Window_Display(*length_window); case WIND_UPDATE: /* Display the list length in the window. */ if ((length = List_Count(demo_list)) <= 10) { sprintf(buffer,"%2.2d",length); Window_MesgPtr(*length_window,2,12,buffer); } break; case WIND_DEL: /* Destroy the window. */ Window_Remove(*length_window); Window_Delete(*length_window); break; } return(0); } /*------------------------------------------------------*/ /* Response_Wind */ /* The window created and maintained by this routine is */ /* used for interaction with the user. It's primary */ /* function is to display single line error messages */ /* related to an illegal command made by the user. */ /*------------------------------------------------------*/ /* Command Response Window Parameters */ #define RESPONSE_ROW 25 #define RESPONSE_COL 1 #define RESPONSE_WIDTH 80 #define RESPONSE_HEIGHT 1 #define RESPONSE_BKCOL TEXT_WHITE #define RESPONSE_TXTCOL TEXT_RED #define RESPONSE_TITLE "" #define RESPONSE_BORDER 0 WORD Response_Wind(response_window, cmd, msg) WINDOW_HEAD **response_window; /* The window pointer to use. */ WORD cmd; /* The window command, INIT, DEL, UPDATE. */ TEXT *msg; /* Message to display in the window. */ { TEXT buffer[81]; switch(cmd) { case WIND_INIT: /* Define and display the window. */ Window_Define(response_window, RESPONSE_ROW, RESPONSE_COL, RESPONSE_HEIGHT, RESPONSE_WIDTH, RESPONSE_BKCOL, RESPONSE_TXTCOL, RESPONSE_TITLE, RESPONSE_BORDER | EXPLODE_BIT | IMPLODE_BIT | POPUP_BIT); Window_Display(*response_window); break; case WIND_UPDATE: /* Display the message, or clear the message line. */ if ((msg == NULL) || (strlen(msg) == 0)) strcpy(buffer," "); else { strncpy(buffer,msg,RESPONSE_WIDTH-1); buffer[RESPONSE_WIDTH-2] = (TEXT)NULL; } while (strlen(buffer) < RESPONSE_WIDTH-2) strcat(buffer," "); Window_MesgPtr(*response_window,1,2,buffer); break; case WIND_DEL: /* Destroy the window. */ Window_Remove(*response_window); Window_Delete(*response_window); break; } return(0); } /*--------------------------------------------------*/ /* EnterListItem */ /* This routine generates a form used to allow the */ /* operator to enter a text string that will become */ /* a list item. This is a good short example of */ /* a form being used. */ /*--------------------------------------------------*/ WORD EnterListItem(item_pointer) UBYTE **item_pointer; /* Returned pointer to string entered. */ { FORM_HEAD *new_form; FORM_FIELD *field_ptr; WORD status=0; /*---------------------------------------*/ /* Allocate memory for the string entry. */ /*---------------------------------------*/ *item_pointer = (UBYTE *)malloc(26*sizeof(UBYTE)); strcpy(*item_pointer,""); /*-------------------------------*/ /* Create the string entry form. */ /*-------------------------------*/ Form_Define(&new_form, 12, 15, 6, 25, TEXT_BROWN, TEXT_BWHITE, "LIST ITEM ENTRY", SINGLE_BORDER_BIT | MOUSE_ENABLE_BIT); Form_Add(new_form, NULL, &field_ptr); Form_PromptAdd(field_ptr, 4, 2, 6, TEXT_BROWN,TEXT_BWHITE, TEXT_BWHITE,TEXT_BROWN,"Item: "); Form_TextAdd(field_ptr, 5, 2, 21, TEXT_BLACK,TEXT_WHITE, TEXT_WHITE,TEXT_BLACK,*item_pointer); /*-------------------------------------*/ /* Execute the form to get user input. */ /*-------------------------------------*/ status = Form_Entry(new_form); /*------------------------------------------------------*/ /* Release the memory if the user aborted string entry. */ /*------------------------------------------------------*/ if (status != 0) free(*item_pointer); /*------------------*/ /* Delete the form. */ /*------------------*/ Form_Delete(new_form); /*---------------------------*/ /* Tell the caller if form */ /* was aborted or completed. */ /*---------------------------*/ return(status); } /*--------------------------------------------------*/ /* List_GetItemNumber */ /* This routine generates a form used to allow the */ /* operator to enter a number of the list item to */ /* be selected. This is a good short example of */ /* a form being used. */ /*--------------------------------------------------*/ WORD List_GetItemNumber(list_num) WORD *list_num; /* Pointer to integer to return number in. */ { FORM_HEAD *new_form; FORM_FIELD *field_ptr; TEXT string_ret[3]; /* Temporary string for number entry. */ WORD status=0; strcpy(string_ret,""); *list_num = 0; /*------------------------*/ /* Create the entry form. */ /*------------------------*/ Form_Define(&new_form, 12, 15, 5, 25, TEXT_BROWN, TEXT_BWHITE, "LIST ITEM NUMBER", SINGLE_BORDER_BIT | MOUSE_ENABLE_BIT); Form_Add(new_form, *IntOnly, &field_ptr); Form_PromptAdd(field_ptr, 4, 2, 8, TEXT_BROWN,TEXT_BWHITE, TEXT_BWHITE,TEXT_BROWN,"Number: "); Form_TextAdd(field_ptr, 4, 11, 2, TEXT_BLACK,TEXT_WHITE, TEXT_WHITE,TEXT_BLACK,string_ret); /*-----------------------*/ /* Now execute the form. */ /*-----------------------*/ status = Form_Entry(new_form); /*-+--------------------------*/ /* Release the form's memory. */ /*----------------------------*/ Form_Delete(new_form); /*--------------------------------------*/ /* Convert the received string into */ /* an integer, if form was not aborted. */ /*--------------------------------------*/ if (status != -1) { if (isdigit(string_ret[0]) != 0) *list_num = (WORD)(string_ret[0] - '0'); if (isdigit(string_ret[1]) != 0) *list_num = (*list_num * 10) + (WORD)(string_ret[1] - '0'); *list_num = *list_num - 1; if (*list_num < 0) *list_num = 0; if (*list_num > 9) *list_num = 9; } /*---------------------------*/ /* Tell the caller if form */ /* was aborted or completed. */ /*---------------------------*/ return(status); }