home *** CD-ROM | disk | FTP | other *** search
- /*
- ** help.c
- **
- ** Pictor, Version 1.51, Copyright (c) 1992-94 SoftCircuits
- ** Redistributed by permission.
- */
-
- #include <stdio.h>
- #include <string.h>
- #include <stdlib.h>
-
- #include "compress.h"
- #include "pictor.h"
-
-
- #define WINROWS 19 /* help window dimensions */
- #define WINCOLS 74
-
- static char signature[] = "PICTORHLP00";
- static char *title = "Help";
-
- static NODE *root_node = NULL;
- static long *help_array = NULL;
- static char **topics_array = NULL;
- static char *topics_buff = NULL;
- static char *comp_buff = NULL,*uncomp_buff = NULL;
- static unsigned num_topics,max_comp,max_uncomp;
- static unsigned topics_comp,topics_uncomp;
-
- static char *hlpfile;
- static COLORSTRUCT *hlpcolors,*msgcolors;
-
- static char buffer[128];
- static int helpready = FALSE;
-
- #define UPDATE_NOUPDATE 0
- #define UPDATE_LINKSEL 1
- #define UPDATE_POSITION 2
- #define UPDATE_NEWTOPIC 3
-
- static unsigned uncomp_size,comp_size;
-
- #define STACK_SIZE 30
- static int stack_count = 0;
- static int topic_stack[STACK_SIZE];
-
- typedef struct _LINK {
- int row,col;
- unsigned pos;
- int len;
- } LINK;
-
- #define LINK_CHAR '|'
- #define MAX_LINKS 30
-
- static LINK link_array[MAX_LINKS];
- static int curr_link,num_links;
-
-
- /*
- ** Lets the user select a help topic from the help index.
- */
- static int helpindex(int *topic)
- {
- extern int _PL_lbcols,_PL_lbcolwidth;
- int result,old_lbcols,old_lbcolwidth;
-
- old_lbcols = _PL_lbcols;
- old_lbcolwidth = _PL_lbcolwidth;
- _PL_lbcols = 3;
- _PL_lbcolwidth = 23;
-
- pushstatus();
- result = listbox(topics_array,num_topics,topic,"Help Index",msgcolors);
- popstatus();
-
- _PL_lbcols = old_lbcols;
- _PL_lbcolwidth = old_lbcolwidth;
-
- return(result);
-
- } /* helpindex */
-
- /*
- ** Compare routine used by bsearch().
- */
- static int compare(const void *item1,const void *item2)
- {
- return(stricmp(*(char **)item1,*(char **)item2));
-
- } /* compare */
-
- /*
- ** Searches the current topic array for the given topic.
- */
- static int lookup_topic(char *topic,int *curr_topic)
- {
- char **ptr;
-
- if(topic == NULL) {
- return(helpindex(curr_topic));
- }
- ptr = bsearch(&topic,topics_array,num_topics,sizeof(char *),compare);
- if(ptr == NULL) {
- sprintf(buffer,"No help available for:\n\"%s\"",topic);
- messagebox(buffer,title,MB_OK,msgcolors);
- return(FALSE);
- }
- *curr_topic = (ptr - topics_array);
-
- return(TRUE);
-
- } /* lookup_topic */
-
- /*
- ** Saves a topic onto the topic stack. If the stack is full, the
- ** topics are scrolled so that the least recently push topic is
- ** removed to make room.
- */
- void push_topic(int topic)
- {
- int i;
-
- /* don't save topic if same as last one */
- if(stack_count && topic_stack[stack_count - 1] == topic)
- return;
-
- if(stack_count >= STACK_SIZE) {
- stack_count--;
- for(i = 0;i < stack_count;i++)
- topic_stack[i] = topic_stack[i + 1];
- }
- topic_stack[stack_count++] = topic;
-
- } /* push_topic */
-
- /*
- ** If the current topic is not the only topic on the stack,
- ** the current topic is discarded and the previous one is returned.
- */
- int pop_topic(int *topic)
- {
- /* must have at least 1 topic besides current one */
- if(stack_count > 1) {
- stack_count -= 2;
- *topic = topic_stack[stack_count];
- return(TRUE);
- }
- return(FALSE);
-
- } /* pop_topic */
-
- /*
- ** Scan foreward the specified number of lines.
- */
- static int scanforeward(unsigned *pos,int lines)
- {
- if(*pos >= uncomp_size) {
- beep();
- return(UPDATE_NOUPDATE);
- }
- while(lines && *pos < uncomp_size) {
- while(uncomp_buff[*pos] != '\n' && *pos < uncomp_size)
- (*pos)++;
- if(*pos < uncomp_size)
- (*pos)++;
- lines--;
- }
- return(UPDATE_POSITION);
-
- } /* scanforeward */
-
- /*
- ** Scans backward the number of specified lines.
- */
- static int scanbackward(unsigned *pos,int lines)
- {
- if(*pos == 0) {
- beep();
- return(UPDATE_NOUPDATE);
- }
- if(*pos == 1) {
- *pos = 0;
- return(UPDATE_POSITION);
- }
- (*pos)--;
- while(lines && *pos > 0) {
- do {
- (*pos)--;
- } while(uncomp_buff[*pos] != '\n' && *pos > 0);
- lines--;
- }
- if(uncomp_buff[*pos] == '\n')
- (*pos)++;
- return(UPDATE_POSITION);
-
- } /* scanbackward */
-
- /*
- ** Main help routines.
- */
- static int _helprun(char *topic,FILE *stream)
- {
- int row,in_link,update = UPDATE_NEWTOPIC,curr_topic = 0;
- unsigned i,done = FALSE,help_pos = 0,old_pos = 0;
- char *ptr;
-
- if(lookup_topic(topic,&curr_topic) == FALSE)
- return(FALSE);
-
- i = wopen(center(WINROWS,_PL_rows),center(WINCOLS,_PL_columns),
- WINROWS,WINCOLS,hlpcolors->normal,WO_SHADOW);
- if(i == FALSE) {
- messagebox("Insufficient memory",title,MB_OK,msgcolors);
- return(FALSE);
- }
-
- pushstatus();
- statusbar("<F1>=Index <Tab>=Select <Enter>=View <Backspace>=Previous"
- " <Escape>=Done");
-
- while(!done) {
- if(update != UPDATE_NOUPDATE) {
- if(update == UPDATE_NEWTOPIC) {
- fseek(stream,help_array[curr_topic],SEEK_SET);
- comp_size = getw(stream);
- uncomp_size = getw(stream);
-
- /* Note: in the unlikey event that the disk is removed */
- /* here, it is possible that fseek will set an EOF */
- /* condition instead of an error. So we will check for */
- /* both. */
-
- if(!ferror(stream) && !feof(stream))
- fread(comp_buff,sizeof(char),comp_size,stream);
-
- if(ferror(stream) || feof(stream)) {
- messagebox("Error reading help file",title,MB_OK,msgcolors);
- clearerr(stream);
- update = UPDATE_NOUPDATE;
- }
- else {
- uncompress((BYTE *)comp_buff,uncomp_size,
- (BYTE *)uncomp_buff,root_node);
- xprintf(wtitle,"Help: %s",topics_array[curr_topic]);
-
- push_topic(curr_topic);
- num_links = old_pos = help_pos = 0;
- update = UPDATE_POSITION;
- }
- }
-
- if(update == UPDATE_POSITION) {
- if(num_links)
- old_pos = link_array[curr_link].pos;
- num_links = 0;
-
- /* display help text */
- for(i = help_pos,row = 1;row <= (WINROWS - 2);row++) {
- in_link = FALSE;
- setwpos(row,1);
- while(i < uncomp_size && uncomp_buff[i] != '\n') {
- if(uncomp_buff[i] == LINK_CHAR && num_links < MAX_LINKS) {
- if(in_link == FALSE) {
- link_array[num_links].row = row;
- link_array[num_links].col = getwcol();
- link_array[num_links].pos = (i + 1);
- in_link = TRUE;
- }
- else {
- link_array[num_links].len = (i -
- link_array[num_links].pos);
- num_links++;
- in_link = FALSE;
- }
- }
- else wputc(uncomp_buff[i]);
- i++;
- }
- wcleareol();
- i++;
- }
-
- /* determine active link */
- if(num_links > 0) {
- curr_link = -1;
- /* make previous link active if still visible */
- for(i = 0;i < (unsigned)num_links;i++) {
- if(link_array[i].pos == old_pos) {
- curr_link = i;
- break;
- }
- }
- /* else determine new active link */
- if(curr_link == -1) {
- if(link_array[0].pos > old_pos)
- curr_link = 0;
- else
- curr_link = (num_links - 1);
- }
- }
- update = UPDATE_LINKSEL;
- }
- if(update == UPDATE_LINKSEL) {
- for(i = 0;i < (unsigned)num_links;i++) {
- setwpos(link_array[i].row,link_array[i].col);
- if(i == (unsigned)curr_link)
- wrepa(hlpcolors->select,link_array[i].len);
- else
- wrepa(hlpcolors->boldnormal,link_array[i].len);
- }
- update = UPDATE_NOUPDATE;
- }
- }
-
- switch(kbdread()) {
- case BACKSPACE_KEY:
- /* pop previous topic off stack */
- if(pop_topic(&curr_topic))
- update = UPDATE_NEWTOPIC;
- else
- beep();
- break;
- case ENTER_KEY:
- if(num_links) {
- ptr = uncomp_buff + link_array[curr_link].pos;
- ptr[link_array[curr_link].len] = '\0';
- if(lookup_topic(ptr,&curr_topic))
- update = UPDATE_NEWTOPIC;
- ptr[link_array[curr_link].len] = LINK_CHAR;
- }
- else beep();
- break;
- case ESCAPE_KEY:
- done = TRUE;
- break;
- case TAB_KEY:
- if(num_links > 0) {
- if(++curr_link > (num_links - 1))
- curr_link = 0;
- update = UPDATE_LINKSEL;
- }
- else beep();
- break;
- case SHIFTTAB_KEY:
- if(num_links > 0) {
- if(--curr_link < 0) {
- curr_link = (num_links - 1);
- }
- update = UPDATE_LINKSEL;
- }
- else beep();
- break;
- case UP_KEY:
- update = scanbackward(&help_pos,1);
- break;
- case DOWN_KEY:
- update = scanforeward(&help_pos,1);
- break;
- case PGUP_KEY:
- update = scanbackward(&help_pos,WINROWS - 2);
- break;
- case PGDN_KEY:
- update = scanforeward(&help_pos,WINROWS - 2);
- break;
- case HOME_KEY:
- if(help_pos > 0) {
- help_pos = 0;
- update = UPDATE_POSITION;
- }
- break;
- case END_KEY:
- help_pos = uncomp_size;
- scanbackward(&help_pos,WINROWS - 3);
- update = UPDATE_POSITION;
- break;
- case F1_KEY:
- if(helpindex(&curr_topic))
- update = UPDATE_NEWTOPIC;
- break;
- default:
- beep();
- break;
- }
- }
- wclose();
- popstatus();
-
- return(TRUE);
-
- } /* _helprun */
-
- /*
- ** Destroys all help data structures and frees memory.
- */
- static void free_helpmem(void)
- {
- if(root_node) {
- freetree(root_node);
- root_node = NULL;
- }
- if(help_array) {
- free(help_array);
- help_array = NULL;
- }
- if(topics_array) {
- free(topics_array);
- topics_array = NULL;
- }
- if(topics_buff) {
- free(topics_buff);
- topics_buff = NULL;
- }
- if(comp_buff) {
- free(comp_buff);
- comp_buff = NULL;
- }
- if(uncomp_buff) {
- free(uncomp_buff);
- uncomp_buff = NULL;
- }
-
- helpready = FALSE;
-
- } /* free_helpmem */
-
- /*
- ** Allocates help structures. Returns TRUE and sets helpready = TRUE
- ** if successful. Returns FALSE if error.
- */
- static int buildhelp(FILE *stream,int *mem_err)
- {
- unsigned i;
- long fpos;
- char *ptr;
-
- if(helpready == TRUE) /* help already allocated */
- free_helpmem();
-
- *mem_err = FALSE;
-
- /* check for valid help file signature */
- fread(buffer,sizeof(char),sizeof(signature),stream);
- if(strcmp(signature,buffer)) {
- sprintf(buffer,"Unrecognized help file format:\n\"%s\"",hlpfile);
- messagebox(buffer,title,MB_OK,msgcolors);
- return(FALSE);
- }
-
- *mem_err = TRUE;
-
- /* read and uncompress code tree */
- ptr = malloc(i = getw(stream));
- if(ptr == NULL)
- return(FALSE);
- fread(ptr,sizeof(char),i,stream);
- root_node = readtree((BYTE *)ptr);
- free(ptr);
- if(root_node == NULL)
- return(FALSE);
-
- /* skip over compressed help text */
- fread(&fpos,sizeof(long),1,stream);
- fseek(stream,fpos,SEEK_SET);
-
- num_topics = getw(stream); /* number of help topics */
- max_comp = getw(stream); /* largest compressed topic */
- max_uncomp = getw(stream); /* largest uncompressed topic */
-
- /* read array of file offsets into help text */
- help_array = malloc(num_topics * sizeof(long));
- if(help_array == NULL)
- return(FALSE);
- fread(help_array,sizeof(long),num_topics,stream);
-
- topics_comp = getw(stream); /* compressed topics size */
- topics_uncomp = getw(stream); /* uncompressed topics size */
-
- /* read and uncompress topic labels */
- ptr = malloc(topics_comp);
- topics_buff = malloc(topics_uncomp);
- if(ptr == NULL || topics_buff == NULL) {
- free(ptr);
- return(FALSE);
- }
- fread(ptr,sizeof(char),topics_comp,stream);
- uncompress((BYTE *)ptr,topics_uncomp,(BYTE *)topics_buff,root_node);
- free(ptr);
-
- /* build array of pointers into help topic labels */
- topics_array = malloc(num_topics * sizeof(char *));
- if(topics_array == NULL)
- return(FALSE);
- for(ptr = topics_buff,i = 0;i < num_topics;i++) {
- topics_array[i] = ptr;
- ptr += (strlen(ptr) + 1);
- }
-
- /* allocate buffers for help text */
- comp_buff = malloc(max_comp);
- uncomp_buff = malloc(max_uncomp);
- if(comp_buff == NULL || uncomp_buff == NULL)
- return(FALSE);
-
- *mem_err = FALSE;
- helpready = TRUE;
- return(TRUE);
-
- } /* buildhelp */
-
- /*
- ** This is the callable portion of runhelp that "wraps" _runhelp.
- */
- int helprun(char *topic)
- {
- static int in_help = FALSE;
- int result,mem_err;
- FILE *stream;
-
- if(in_help || _PL_helpfunc == NULL) {
- beep();
- return(FALSE);
- }
-
- in_help = TRUE;
- pushstatus();
-
- /* open help file */
- if((stream = fopen(hlpfile,"rb")) == NULL) {
- sprintf(buffer,"Help file not found:\n\"%s\"",hlpfile);
- messagebox(buffer,title,MB_OK,msgcolors);
- popstatus();
- in_help = FALSE;
- return(FALSE);
- }
-
- /* build help data structures if needed */
- if(helpready == FALSE) {
- if(!buildhelp(stream,&mem_err)) {
- if(mem_err)
- messagebox("Insufficient memory",title,MB_OK,msgcolors);
- fclose(stream);
- free_helpmem();
- popstatus();
- in_help = FALSE;
- return(FALSE);
- }
- }
-
- result = _helprun(topic,stream);
-
- fclose(stream);
- popstatus();
- in_help = FALSE;
-
- return(result);
-
- } /* helprun */
-
- /*
- ** Frees help memory and makes help inactive.
- */
- void helpclose(void)
- {
- free_helpmem();
- _PL_helpfunc = NULL;
-
- } /* helpclose */
-
- /*
- ** Defines help parameters and activates the help system.
- */
- void helpopen(char *filename,COLORSTRUCT *hcolors,COLORSTRUCT *mcolors)
- {
- /* make sure help is not active */
- if(_PL_helpfunc != NULL)
- helpclose();
-
- hlpfile = filename;
- hlpcolors = hcolors;
- msgcolors = mcolors;
-
- _PL_helpfunc = helprun;
-
- stack_count = 0; /* clear topic history */
-
- } /* helpopen */
-