/*-----------------------------------------------------------------------*/ /* FSEL.C, a point and shoot file selector */ /*-----------------------------------------------------------------------*/ FSEL should recieve the entry points for two subroutines a) to determine whether a group of files with the same filename constitute a valid dataset b) to open a dataset, defined as opening at least one file, possibly renaming and deleting others, but ultimately leaving one file open, whose low level handle will be returned via FSEL. Attributes will be specified in the the opening routine, although one control word may be passed through. since fsel does not concern itself with what is a dataset or how to open it, it can be used for general purpose. 1. clear the screen 2. post an edited file list (.txt and drives or directories only) should scrolling be implemented? use the cursor to walk around it, or to the backout icon, or to the creat new field if it is complete, do it, else repost the screen and repeat the create new field should take a path\file sequence restore the screen? /*-----------------------------------------------------------------*/ struct lst_entry { char ff_attrib; char ff_name[9]; char ff_ext[5]; } LIST_ENTRY; struct fsel_dat_type { /*------------------------------------------------------*/ LIST_ENTRY f_list[LIST_SIZE]; /*the choice list */ int num_items_in_list; /*------------------------------------------------------*/ int file_under_cursor; /*selector status data */ int is_in_entry_blank_q; int dir_update; int file_menu_update; char temp_path[80]; char temp_name[13]; int file_menu_offset; /* in files */ /*------------------------------------------------------*/ ?????? *fsel_style_routine_ptr(); /*entry point pointer*/ } fsel_dat; /* the structure fsel_dat holds all data shared among the functions of */ /* the file selector */ /*-----------------------------------------------------------------*/ /* choice list generation section */ /*-----------------------------------------------------------------*/ /*---------------------------------------------------------------------*/ get_choice_list( struct lst_entry f_list[], char *start_path, int rec_size, int *last_file_in_list ) { int i; char s1[80], s2[80]; if(startpath == NULL) /* if the extended filename is completely empty */ list_drives(f_list, last_file_in_list); else { path_rectify(&s1, start_path); list_direct(f_list, last_file_in_list, LIST_SIZE, wild_card(&s1, &s2)); qsort(list_entry, *last_file_in_list+1, sizeof(lst_entry),srt_function); reduce_list(f_list, last_file_in_list, file_valid); } } /*---------------*/ list_drives( struct lst_entry f_list[], int *last_file_in_list ) { char s[3]; *last_file_in_list=0; for(i=drv_nam_to_int("A:"); i <= drv_nam_to_int("Z:"); i++) if(is_valid_drive_name(drv_num_to_str(&s,i))) set_list_entry(f_list, last_file_in_list, &s, NULL, FA_DIREC); } /*---------------*/ list_direct( struct lst_entry f_list[], int *last_file_in_list, int m_num, char *specpath ) { char dummy[80]; int r; int s_attrib=FA_NORMAL|FA_DIREC; struct ffblk dir_entry; for(*last_file_in_list=0; *last_file_in_list < m_num; *last_file_in_list++) { if(*last_file_in_list == 0) r=findfirst(specpath,dir_entry,s_attrib); else r= findnext(dir_entry); if(r == N_ERR) { f_list[*last_file_in_list].ff_attrib=dir_entry.ff_attrib; fnsplit( dir_entry.ff_name, &dummy, &dummy, f_list[*last_file_in_list].ff_name, f_list[*last_file_in_list].ff_ext ); } else break; } } /*------------------------------------------------------------*/ srt_function(lst_entry *entry1, lst_entry *entry2) { /* sort them in the following order */ int a,b; if( a=entry1.ff_attrib&FA_DIRECT /* directories in colating sequence */ != b=entry2.ff_attrib&FA_DIRECT ) { if(a != 0) return(-1) else return(1) } else if(c=strcmp(entry1.ff_name, entry2.ff_name) !=0) return(c); /* files by filename */ else return( strcmp(entry1.ff_ext, entry2.ff_ext)); /* within that by extension */ } /*----------------------------------*/ reduce_list( struct lst_entry f_list[], int *last_file_in_list, int (*file_valid)() ); { int first_file_in_list; int next_item_to_scan; int next_slot_to_fill; for(i=0; i <= *last_file_in_list; i++) if(f_list[i].ff_attrib&FA_DIRECT != 0) /* find the first nondirctory */ { next_slot_to_fill = first_file_in_list = i; break; } for(i = first_file_in_list; i <= *last_file_in_list; i++) { first_extent_for_file=i; while( ( strcmp( f_list[first_extent_for_file].ff_name, f_list[i].ff_name) == 0 ) && (i <= *last_file_in_list) ) i++; /* for all files of the same filename */ if( (file_valid)( LG_YES /* have name */ f_list[first_extent_for_file].ff_ext, sizeof(LIST_ENTRY), /* the record's size */ (i+1-first_extent_for_file), /* the number of records */ &proper_ext ) ) set_list_entry( f_list, &next_slot_to_fill, f_list[first_extent_for_file].ff_name, proper_ext, FA_NORMAL ); } /*----------------*/ set_list_entry( struct lst_entry f_list[], int *counter, char *f_name, char *f_ext, int f_attr ) { strcpy(f_list[*counter]->ff_ext, f_ext); strcpy(f_list[*counter]->ff_name, f_name); f_list[*counter]->ff_attrib=f_attr; *counter++; } /*--------------------------------------------------------------------*/ -- divide the screen into cells the screen consists of a) cells refering to possible choices b) the write-in space, the starting point of a screen, entering or deleting a delimiter (:,\) should generate a new screen and put one in a position to go on entering c) the previous directory option, in which case you pop a name level off --------------- /*---------------------------------------------------------------------*/ // mark this as local main// // change the program to agree with parm changes getfile( int f_nm_pres_q, /* file name already present */ int prmpt_cre_q, /* prompt for permission to create a file? */ char *iprompt, /* the initial prompt */ char *filename, /* holds the filename, and is altered or not */ void *f_sel_parm, /* pointer to a parameter block for the */ /* passed routines */ int (*file_open)(), /* args:char *filename, */ /* void *f_sel_parm */ /* (shared with file_valid) */ /* routine to open file */ /* must return a file descriptor */ /* or R_ERR for failure */ int (*file_valid)() /* args:int have_name, LG_YES or LG_NO */ /* void *base, first extension \ */ /* int r_len record length / */ /* int n_rec number of records/ */ /* char *t_ext proper extension */ /* routine to validate dataset name, specify */ /* extension for new one, */ /* returns LG_YES or LG_NO according to whether*/ /* the extensions constitute a valid dataset */ int (*get_ch_mov)() /* a function with equivalent arguments to the*/ /* provided 'get_ch_mov */ ) /* returns file descriptor of opened file if sucessful */ /* or R_ERR (-1) if not */ { int dhandle; int name_ready_q = f_nm_pres_q; for(;;) { if(!name_ready_q) /* prompt for a filename */ { if(getname(fsel_dat, nkey) == R_ERR) return(R_ERR); } /* if we get this far, the name is axiomatically ready */ if( (dhandle=(*file_open)(filename, f_sel_parm)) != R_ERR ) return(dhandle); /*return if good */ else name_ready_q = LG_NO; } } /* Prompt for filename. If you get a valid filename, */ /* create it if necessary. Prompt again until the getname fails */ /* or a valid file is generated */ /* return handle for sucess, R_ERR for failure */ ------------------------- getname(fsel_dat, nkey) { fsel_setup(fsel_dat); for(;;) { fsel_update(fsel_dat); get_mov_ch(n_key, 8, 32); if(n_key.have_move_q) place_cursor(fsel_dat, n_key); x=is_select(n_key, LG_YES); if(x == R_ERR) return(R_ERR); else if(x == LG_YES) { post_selection(fsel_dat); return(N_ERR); } else if(x == LG_NO) post_char(fsel_dat, n_key.charin); else /* something must have gone wrong */ return(R_ERR); } } ----------------- subfunctions are: fsel_update(fsd) /*makes the screen, other data, conform to temp_path, file_under_cursor */ { /*--------------- clear the cursor ------------------------*/ write_file_menu_entry(fsd, file_under_cursor, UNMARKED) /*--------------- get a new offset if necessary -----------*/ if( regulate_offset( fsd->is_in_entry_blank_q, fsd->file_menu_offset, fsd->file_under_cursor, 16, 40 /*number of items in window*/ ) == R_ERR ) fsd->file_menu_update = LG_YES; /*------------ get a new choice list if appropriate --------*/ if(fsd->dir_update == LG_YES) { get_choice_list(.....); fsd->dir_update=LG_NO; set_up_screen(.....); put path string fsd->file_menu_update=LG_YES; fsd->is_in_entry_blank_q=LG_YES; fsd->file_menu_offset=0; fsd->file_under_cursor=0; } /*---------- display_file_list(fsd) } -------------------- display_file_list(struct fsel_dat_type *fsd) { if(fsd->file_menu_update == LG_YES) { write_file_menu_slist(fsd) } else /* if not update */ if(!fsd->is_in_entry_blank_q) write_file_menu_entry(fsd, file_under_cursor, MARKED); } reads file_menu_update, and performs a major or minor update of the file entry blank and file menu. If it mucks with the menu, except to change the highlighting (minor update) then it replaces it wholesale. write_file_menu_entry(fsd, item, attribute) ----------------------------------------------- write_file_menu_slist(fsd) { for( i=fsd->file_menu_offset; (i < fsd->num_items_in_list) && (i < fsd->file_menu_offset+40); i++ ) { if( (i == fsd->file_under_cursor) && (!fsd->is_in_entry_blank_q) ) item_attrib=MARKED; else item_attrib=UNMARKED; write_file_menu_entry(fsd, i, item_attrib); } } -------------------------------------------- regulate_offset( int *enforce_0_offset_q, int *c_offset, /*this is the output*/ int *c_item, /*item under cursor*/ first_scrollable_item, /* the closest you can get to the top of the list while remaining scrolled */ window_size /* in items */ ) { int n_offset; int rcode = N_ERR; if( (*c_offset != 0) && ( (*enforce_0_offset_q) || (*c_item < first_scrollable_item) ) ) { rcode = R_ERR; *c_offset = 0; } else if( ( ! *enforce_0_offset_q) && ( n_offset = iclamp(*c_offset, *c_item-window_size+1, *c_item ) != *c_offset ) ) { *c_offset=n_offset; rcode=R_ERR; } return(rcode); } ---------------------------------------------- place_cursor( struct fsel_dat_type *fsd, strct key_in *n_key ) /* positions the file cursor correctly and sets the modes */ /* but doesn't do anything about putting them on the screen */ { /* Does NOT do ANYTHING about scrolling the menu */ int new_file_under_cursor; if(fsd->is_in_entry_blank_q) { if(n_key.down_move > 0) { fsd->is_in_entry_blank_q=LG_NO; fsd->file_under_cursor=0; /* go to top left of file menu */ } } else { if( /* if the move doesn't skip off the end of a line */ inrange((fsd->file_under_cursor%4+n_key.right_move), '=', 0, '<',4) ) { new_file_under_cursor = fsd->file_under_cursor + 4*n_key.down_move + n_key.right_move; if(new_file_under_cursor < 0) { fsd->is_in_entry_blank_q= LG_YES; } else if(new_file_under_cursor < fsd->num_items_in_list) fsd->file_under_cursor = new_file_under_cursor; } } } if in the file menu, it can go in any direction if it goes off the bottom, that produces scrolling when it comes back up,it is not allowed to get closer than 4 spaces to the top of the apparent window until the manu is scrolled home again ---------------- post_selection(struct fsel_dat_type *fsd) /* adds the selection to temp_path */ { if(is_in_entry_blank_q) { strcat(fsd->temp_path, fsd->temp_name); indicate sucess } else /* if not is_in_entry_blank */ { /* take the selection under the cursor */ strcat( fsd->temp_path, (fsd->f_list[fsd->file_under_cursor].ff_name ); if((fsd->flist[fsd->file_under_cursor].ff_attrib) & FA_DIREC) { strcat(fsd->temp_path, "\\"); fsd->dir_update=LG_YES; } else /* if it really is a file */ { strcat( fsd->temp_path, (fsd->f_list[fsd->file_under_cursor].ff_ext ); indicate sucess } } } --- posts a printable character or backspace. employs home_to_blank(fsel_dat) to make sure the menu is scrolled back, and the cursor in the entry blank /*-------------------------------------*/ 4 entries wide sequence: is valid move undo marking move and if necessary scroll (do this by rewriting in toto) re-mark /*------------------------------------*/ post_char( struct fsel_dat_type *fsd, char c ) { fsd->is_in_entry_blank_q=LG_YES; switch(c) { case '\b': /* try to delete a character from the filename */ if(pop_ch_delim_fr_name_str(fsd->temp_name) == R_ERR) { /* and if that doesn't work, do it from the path instead */ pop_ch_delim_fr_name_str(fsd->temp_path) fsd->dir_update=LG_YES; } break; case '\\': case ':' : if(strchr(fsd->temp_name, '.') == NULL) /* assuming a period does not precede the delimiter */ { fsd->dir_update=LG_YES; strcat(fsd->temp_path, fsd->temp_name); fsd->temp_name[0]='\0'; cstrcat(fsd->temp_path, c); /* add charin to filename */ } break; case '.': if(strchr(fsd-temp_name, '.') == NULL) cstrcat( fsd->temp_name, c); break; default: if(nm_add_chs_permit_in_f_nm > 0) if(is_file_n_char(c)) cstrcat( fsd->temp_name, toupper(c) ); break; /* ignores chars that don't make a valid file name */ } } /*----------------------------------------------------------------------*/ int nm_add_chs_permit_in_f_nm(char *fn) { char *p; int l; l=strlen(fn); p=strchr(fn, '.') if(p == NULL) return(8-l); else return(4-((fn+l)-p)); } /*-----------------------------------------*/ int pop_ch_delim_fr_name_str(char *s) { int last_ch, j; if(s == NULL) return(R_ERR); else /* if there is something in the string */ { last_ch=strlen(s)-1; if( (s[last_ch] != '\\') && (s[last_ch] != ':') /* if the last character is not a delimiter */ ) s[i]='\0'; /* pop it */ else /* if it is a delimiter */ for(j=last_ch; j >= 0; j--) { if(s[j] == '\\') s[j]='\0'; /* pop off backslashes */ else if(s[j] == ':') { s[0]='\0'; /* empty the string */ break; } else /*if it is a nondelimiter */ break; } return(N_ERR); } } /* pops a character or a continuous cluster of delimiters from the end */ /* of the string. If a colon is encountered, the string is nulled */ /*----------------------------------------------------------------------*/