home *** CD-ROM | disk | FTP | other *** search
/ Stars of Shareware: Programmierung / SOURCE.mdf / programm / msdos / c / pictor15 / libsrc / help.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-03-15  |  13.1 KB  |  588 lines

  1. /*
  2. ** help.c
  3. **
  4. ** Pictor, Version 1.51, Copyright (c) 1992-94 SoftCircuits
  5. ** Redistributed by permission.
  6. */
  7.  
  8. #include <stdio.h>
  9. #include <string.h>
  10. #include <stdlib.h>
  11.  
  12. #include "compress.h"
  13. #include "pictor.h"
  14.  
  15.  
  16. #define WINROWS   19    /* help window dimensions */
  17. #define WINCOLS   74
  18.  
  19. static char signature[] = "PICTORHLP00";
  20. static char *title = "Help";
  21.  
  22. static NODE *root_node = NULL;
  23. static long *help_array = NULL;
  24. static char **topics_array = NULL;
  25. static char *topics_buff = NULL;
  26. static char *comp_buff = NULL,*uncomp_buff = NULL;
  27. static unsigned num_topics,max_comp,max_uncomp;
  28. static unsigned topics_comp,topics_uncomp;
  29.  
  30. static char *hlpfile;
  31. static COLORSTRUCT *hlpcolors,*msgcolors;
  32.  
  33. static char buffer[128];
  34. static int helpready = FALSE;
  35.  
  36. #define UPDATE_NOUPDATE 0
  37. #define UPDATE_LINKSEL  1
  38. #define UPDATE_POSITION 2
  39. #define UPDATE_NEWTOPIC 3
  40.  
  41. static unsigned uncomp_size,comp_size;
  42.  
  43. #define STACK_SIZE   30
  44. static int stack_count = 0;
  45. static int topic_stack[STACK_SIZE];
  46.  
  47. typedef struct _LINK {
  48.     int row,col;
  49.     unsigned pos;
  50.     int len;
  51. } LINK;
  52.  
  53. #define LINK_CHAR    '|'
  54. #define MAX_LINKS    30
  55.  
  56. static LINK link_array[MAX_LINKS];
  57. static int curr_link,num_links;
  58.  
  59.  
  60. /*
  61. ** Lets the user select a help topic from the help index.
  62. */
  63. static int helpindex(int *topic)
  64. {
  65.     extern int _PL_lbcols,_PL_lbcolwidth;
  66.     int result,old_lbcols,old_lbcolwidth;
  67.  
  68.     old_lbcols = _PL_lbcols;
  69.     old_lbcolwidth = _PL_lbcolwidth;
  70.     _PL_lbcols = 3;
  71.     _PL_lbcolwidth = 23;
  72.  
  73.     pushstatus();
  74.     result = listbox(topics_array,num_topics,topic,"Help Index",msgcolors);
  75.     popstatus();
  76.  
  77.     _PL_lbcols = old_lbcols;
  78.     _PL_lbcolwidth = old_lbcolwidth;
  79.  
  80.     return(result);
  81.  
  82. } /* helpindex */
  83.  
  84. /*
  85. ** Compare routine used by bsearch().
  86. */
  87. static int compare(const void *item1,const void *item2)
  88. {
  89.     return(stricmp(*(char **)item1,*(char **)item2));
  90.  
  91. } /* compare */
  92.  
  93. /*
  94. ** Searches the current topic array for the given topic.
  95. */
  96. static int lookup_topic(char *topic,int *curr_topic)
  97. {
  98.     char **ptr;
  99.  
  100.     if(topic == NULL) {
  101.         return(helpindex(curr_topic));
  102.     }
  103.     ptr = bsearch(&topic,topics_array,num_topics,sizeof(char *),compare);
  104.     if(ptr == NULL) {
  105.         sprintf(buffer,"No help available for:\n\"%s\"",topic);
  106.         messagebox(buffer,title,MB_OK,msgcolors);
  107.         return(FALSE);
  108.     }
  109.     *curr_topic = (ptr - topics_array);
  110.  
  111.     return(TRUE);
  112.  
  113. } /* lookup_topic */
  114.  
  115. /*
  116. ** Saves a topic onto the topic stack. If the stack is full, the
  117. ** topics are scrolled so that the least recently push topic is
  118. ** removed to make room.
  119. */
  120. void push_topic(int topic)
  121. {
  122.     int i;
  123.  
  124.     /* don't save topic if same as last one */
  125.     if(stack_count && topic_stack[stack_count - 1] == topic)
  126.         return;
  127.  
  128.     if(stack_count >= STACK_SIZE) {
  129.         stack_count--;
  130.         for(i = 0;i < stack_count;i++)
  131.             topic_stack[i] = topic_stack[i + 1];
  132.     }
  133.     topic_stack[stack_count++] = topic;
  134.  
  135. } /* push_topic */
  136.  
  137. /*
  138. ** If the current topic is not the only topic on the stack,
  139. ** the current topic is discarded and the previous one is returned.
  140. */
  141. int pop_topic(int *topic)
  142. {
  143.     /* must have at least 1 topic besides current one */
  144.     if(stack_count > 1) {
  145.         stack_count -= 2;
  146.         *topic = topic_stack[stack_count];
  147.         return(TRUE);
  148.     }
  149.     return(FALSE);
  150.  
  151. } /* pop_topic */
  152.  
  153. /*
  154. ** Scan foreward the specified number of lines.
  155. */
  156. static int scanforeward(unsigned *pos,int lines)
  157. {
  158.     if(*pos >= uncomp_size) {
  159.         beep();
  160.         return(UPDATE_NOUPDATE);
  161.     }
  162.     while(lines && *pos < uncomp_size) {
  163.         while(uncomp_buff[*pos] != '\n' && *pos < uncomp_size)
  164.             (*pos)++;
  165.         if(*pos < uncomp_size)
  166.             (*pos)++;
  167.         lines--;
  168.     }
  169.     return(UPDATE_POSITION);
  170.  
  171. } /* scanforeward */
  172.  
  173. /*
  174. ** Scans backward the number of specified lines.
  175. */
  176. static int scanbackward(unsigned *pos,int lines)
  177. {
  178.     if(*pos == 0) {
  179.         beep();
  180.         return(UPDATE_NOUPDATE);
  181.     }
  182.     if(*pos == 1) {
  183.         *pos = 0;
  184.         return(UPDATE_POSITION);
  185.     }
  186.     (*pos)--;
  187.     while(lines && *pos > 0) {
  188.         do {
  189.             (*pos)--;
  190.         } while(uncomp_buff[*pos] != '\n' && *pos > 0);
  191.         lines--;
  192.     }
  193.     if(uncomp_buff[*pos] == '\n')
  194.         (*pos)++;
  195.     return(UPDATE_POSITION);
  196.  
  197. } /* scanbackward */
  198.  
  199. /*
  200. ** Main help routines.
  201. */
  202. static int _helprun(char *topic,FILE *stream)
  203. {
  204.     int row,in_link,update = UPDATE_NEWTOPIC,curr_topic = 0;
  205.     unsigned i,done = FALSE,help_pos = 0,old_pos = 0;
  206.     char *ptr;
  207.  
  208.     if(lookup_topic(topic,&curr_topic) == FALSE)
  209.         return(FALSE);
  210.  
  211.     i = wopen(center(WINROWS,_PL_rows),center(WINCOLS,_PL_columns),
  212.         WINROWS,WINCOLS,hlpcolors->normal,WO_SHADOW);
  213.     if(i == FALSE) {
  214.         messagebox("Insufficient memory",title,MB_OK,msgcolors);
  215.         return(FALSE);
  216.     }
  217.     
  218.     pushstatus();
  219.     statusbar("<F1>=Index  <Tab>=Select  <Enter>=View  <Backspace>=Previous"  
  220.         "  <Escape>=Done");
  221.  
  222.     while(!done) {
  223.         if(update != UPDATE_NOUPDATE) {
  224.             if(update == UPDATE_NEWTOPIC) {
  225.                 fseek(stream,help_array[curr_topic],SEEK_SET);
  226.                 comp_size = getw(stream);
  227.                 uncomp_size = getw(stream);
  228.  
  229.                 /* Note: in the unlikey event that the disk is removed */
  230.                 /* here, it is possible that fseek will set an EOF */
  231.                 /* condition instead of an error. So we will check for */
  232.                 /* both. */
  233.  
  234.                 if(!ferror(stream) && !feof(stream))
  235.                     fread(comp_buff,sizeof(char),comp_size,stream);
  236.  
  237.                 if(ferror(stream) || feof(stream)) {
  238.                     messagebox("Error reading help file",title,MB_OK,msgcolors);
  239.                     clearerr(stream);
  240.                     update = UPDATE_NOUPDATE;
  241.                 }
  242.                 else {
  243.                     uncompress((BYTE *)comp_buff,uncomp_size,
  244.                         (BYTE *)uncomp_buff,root_node);
  245.                     xprintf(wtitle,"Help: %s",topics_array[curr_topic]);
  246.  
  247.                     push_topic(curr_topic);
  248.                     num_links = old_pos = help_pos = 0;
  249.                     update = UPDATE_POSITION;
  250.                 }
  251.             }
  252.             
  253.             if(update == UPDATE_POSITION) {
  254.                 if(num_links) 
  255.                     old_pos = link_array[curr_link].pos;
  256.                 num_links = 0;
  257.                 
  258.                 /* display help text */
  259.                 for(i = help_pos,row = 1;row <= (WINROWS - 2);row++) {
  260.                     in_link = FALSE;
  261.                     setwpos(row,1);
  262.                     while(i < uncomp_size && uncomp_buff[i] != '\n') {
  263.                         if(uncomp_buff[i] == LINK_CHAR && num_links < MAX_LINKS) {
  264.                             if(in_link == FALSE) {
  265.                                 link_array[num_links].row = row;
  266.                                 link_array[num_links].col = getwcol();
  267.                                 link_array[num_links].pos = (i + 1);
  268.                                 in_link = TRUE;
  269.                             }
  270.                             else {
  271.                                 link_array[num_links].len = (i - 
  272.                                     link_array[num_links].pos);
  273.                                 num_links++;
  274.                                 in_link = FALSE;
  275.                             }
  276.                         }
  277.                         else wputc(uncomp_buff[i]);
  278.                         i++;
  279.                     }
  280.                     wcleareol();
  281.                     i++;
  282.                 }
  283.  
  284.                 /* determine active link */
  285.                 if(num_links > 0) {
  286.                     curr_link = -1;
  287.                     /* make previous link active if still visible */
  288.                     for(i = 0;i < (unsigned)num_links;i++) {
  289.                         if(link_array[i].pos == old_pos) {
  290.                             curr_link = i;
  291.                             break;
  292.                         }
  293.                     }
  294.                     /* else determine new active link */
  295.                     if(curr_link == -1) {
  296.                         if(link_array[0].pos > old_pos)
  297.                             curr_link = 0;
  298.                         else
  299.                             curr_link = (num_links - 1);
  300.                     }
  301.                 }
  302.                 update = UPDATE_LINKSEL;
  303.             }
  304.             if(update == UPDATE_LINKSEL) {
  305.                 for(i = 0;i < (unsigned)num_links;i++) {
  306.                     setwpos(link_array[i].row,link_array[i].col);
  307.                     if(i == (unsigned)curr_link)
  308.                         wrepa(hlpcolors->select,link_array[i].len);
  309.                     else
  310.                         wrepa(hlpcolors->boldnormal,link_array[i].len);
  311.                 }
  312.                 update = UPDATE_NOUPDATE;
  313.             }
  314.         }
  315.  
  316.         switch(kbdread()) {
  317.             case BACKSPACE_KEY:
  318.                 /* pop previous topic off stack */
  319.                 if(pop_topic(&curr_topic))
  320.                     update = UPDATE_NEWTOPIC;
  321.                 else
  322.                     beep();
  323.                 break;
  324.             case ENTER_KEY:
  325.                 if(num_links) {
  326.                     ptr = uncomp_buff + link_array[curr_link].pos;
  327.                     ptr[link_array[curr_link].len] = '\0';
  328.                     if(lookup_topic(ptr,&curr_topic))
  329.                         update = UPDATE_NEWTOPIC;
  330.                     ptr[link_array[curr_link].len] = LINK_CHAR;
  331.                 }
  332.                 else beep();
  333.                 break;
  334.             case ESCAPE_KEY:
  335.                 done = TRUE;
  336.                 break;
  337.             case TAB_KEY:
  338.                 if(num_links > 0) {
  339.                     if(++curr_link > (num_links - 1))
  340.                         curr_link = 0;
  341.                     update = UPDATE_LINKSEL;
  342.                 }
  343.                 else beep();
  344.                 break;
  345.             case SHIFTTAB_KEY:
  346.                 if(num_links > 0) {
  347.                     if(--curr_link < 0) {
  348.                         curr_link = (num_links - 1);
  349.                     }
  350.                     update = UPDATE_LINKSEL;
  351.                 }
  352.                 else beep();
  353.                 break;
  354.             case UP_KEY:
  355.                 update = scanbackward(&help_pos,1);
  356.                 break;
  357.             case DOWN_KEY:
  358.                 update = scanforeward(&help_pos,1);
  359.                 break;
  360.             case PGUP_KEY:
  361.                 update = scanbackward(&help_pos,WINROWS - 2);
  362.                 break;
  363.             case PGDN_KEY:
  364.                 update = scanforeward(&help_pos,WINROWS - 2);
  365.                 break;
  366.             case HOME_KEY:
  367.                 if(help_pos > 0) {
  368.                     help_pos = 0;
  369.                     update = UPDATE_POSITION;
  370.                 }
  371.                 break;
  372.             case END_KEY:
  373.                 help_pos = uncomp_size;
  374.                 scanbackward(&help_pos,WINROWS - 3);
  375.                 update = UPDATE_POSITION;
  376.                 break;
  377.             case F1_KEY:
  378.                 if(helpindex(&curr_topic))
  379.                     update = UPDATE_NEWTOPIC;
  380.                 break;
  381.             default:
  382.                 beep();
  383.                 break;
  384.         }
  385.     }
  386.     wclose();
  387.     popstatus();
  388.  
  389.     return(TRUE);
  390.  
  391. } /* _helprun */
  392.  
  393. /*
  394. ** Destroys all help data structures and frees memory.
  395. */
  396. static void free_helpmem(void)
  397. {
  398.     if(root_node) {
  399.         freetree(root_node);
  400.         root_node = NULL;
  401.     }
  402.     if(help_array) {
  403.         free(help_array);
  404.         help_array = NULL;
  405.     }
  406.     if(topics_array) {
  407.         free(topics_array);
  408.         topics_array = NULL;
  409.     }
  410.     if(topics_buff) {
  411.         free(topics_buff);
  412.         topics_buff = NULL;
  413.     }
  414.     if(comp_buff) {
  415.         free(comp_buff);
  416.         comp_buff = NULL;
  417.     }
  418.     if(uncomp_buff) {
  419.         free(uncomp_buff);
  420.         uncomp_buff = NULL;
  421.     }
  422.  
  423.     helpready = FALSE;
  424.  
  425. } /* free_helpmem */
  426.  
  427. /*
  428. ** Allocates help structures. Returns TRUE and sets helpready = TRUE
  429. ** if successful. Returns FALSE if error.
  430. */
  431. static int buildhelp(FILE *stream,int *mem_err)
  432. {
  433.     unsigned i;
  434.     long fpos;
  435.     char *ptr;
  436.  
  437.     if(helpready == TRUE)      /* help already allocated */
  438.         free_helpmem();
  439.  
  440.     *mem_err = FALSE;
  441.  
  442.     /* check for valid help file signature */
  443.     fread(buffer,sizeof(char),sizeof(signature),stream);
  444.     if(strcmp(signature,buffer)) {
  445.         sprintf(buffer,"Unrecognized help file format:\n\"%s\"",hlpfile);
  446.         messagebox(buffer,title,MB_OK,msgcolors);
  447.         return(FALSE);
  448.     }
  449.  
  450.     *mem_err = TRUE;
  451.  
  452.     /* read and uncompress code tree */
  453.     ptr = malloc(i = getw(stream));
  454.     if(ptr == NULL)
  455.         return(FALSE);
  456.     fread(ptr,sizeof(char),i,stream);
  457.     root_node = readtree((BYTE *)ptr);
  458.     free(ptr);
  459.     if(root_node == NULL)
  460.         return(FALSE);
  461.  
  462.     /* skip over compressed help text */
  463.     fread(&fpos,sizeof(long),1,stream);
  464.     fseek(stream,fpos,SEEK_SET);
  465.  
  466.     num_topics = getw(stream);    /* number of help topics */
  467.     max_comp = getw(stream);      /* largest compressed topic */
  468.     max_uncomp = getw(stream);    /* largest uncompressed topic */
  469.  
  470.     /* read array of file offsets into help text */
  471.     help_array = malloc(num_topics * sizeof(long));
  472.     if(help_array == NULL)
  473.         return(FALSE);
  474.     fread(help_array,sizeof(long),num_topics,stream);
  475.  
  476.     topics_comp = getw(stream);   /* compressed topics size */
  477.     topics_uncomp = getw(stream); /* uncompressed topics size */
  478.  
  479.     /* read and uncompress topic labels */
  480.     ptr = malloc(topics_comp);
  481.     topics_buff = malloc(topics_uncomp);
  482.     if(ptr == NULL || topics_buff == NULL) {
  483.         free(ptr);
  484.         return(FALSE);
  485.     }
  486.     fread(ptr,sizeof(char),topics_comp,stream);
  487.     uncompress((BYTE *)ptr,topics_uncomp,(BYTE *)topics_buff,root_node);
  488.     free(ptr);
  489.  
  490.     /* build array of pointers into help topic labels */
  491.     topics_array = malloc(num_topics * sizeof(char *));
  492.     if(topics_array == NULL)
  493.         return(FALSE);
  494.     for(ptr = topics_buff,i = 0;i < num_topics;i++) {
  495.         topics_array[i] = ptr;
  496.         ptr += (strlen(ptr) + 1);
  497.     }
  498.  
  499.     /* allocate buffers for help text */
  500.     comp_buff = malloc(max_comp);
  501.     uncomp_buff = malloc(max_uncomp);
  502.     if(comp_buff == NULL || uncomp_buff == NULL)
  503.         return(FALSE);
  504.  
  505.     *mem_err = FALSE;
  506.     helpready = TRUE;
  507.     return(TRUE);
  508.  
  509. } /* buildhelp */
  510.  
  511. /*
  512. ** This is the callable portion of runhelp that "wraps" _runhelp.
  513. */
  514. int helprun(char *topic)
  515. {
  516.     static int in_help = FALSE;
  517.     int result,mem_err;
  518.     FILE *stream;
  519.  
  520.     if(in_help || _PL_helpfunc == NULL) {
  521.         beep();
  522.         return(FALSE);
  523.     }
  524.  
  525.     in_help = TRUE;
  526.     pushstatus();
  527.  
  528.     /* open help file */
  529.     if((stream = fopen(hlpfile,"rb")) == NULL) {
  530.         sprintf(buffer,"Help file not found:\n\"%s\"",hlpfile);
  531.         messagebox(buffer,title,MB_OK,msgcolors);
  532.         popstatus();
  533.         in_help = FALSE;
  534.         return(FALSE);
  535.     }
  536.  
  537.     /* build help data structures if needed */
  538.     if(helpready == FALSE) {
  539.         if(!buildhelp(stream,&mem_err)) {
  540.             if(mem_err)
  541.                 messagebox("Insufficient memory",title,MB_OK,msgcolors);
  542.             fclose(stream);
  543.             free_helpmem();
  544.             popstatus();
  545.             in_help = FALSE;
  546.             return(FALSE);
  547.         }
  548.     }
  549.  
  550.     result = _helprun(topic,stream);
  551.  
  552.     fclose(stream);
  553.     popstatus();
  554.     in_help = FALSE;
  555.     
  556.     return(result);
  557.  
  558. } /* helprun */
  559.  
  560. /*
  561. ** Frees help memory and makes help inactive.
  562. */
  563. void helpclose(void)
  564. {
  565.     free_helpmem();
  566.     _PL_helpfunc = NULL;
  567.  
  568. } /* helpclose */
  569.  
  570. /*
  571. ** Defines help parameters and activates the help system.
  572. */
  573. void helpopen(char *filename,COLORSTRUCT *hcolors,COLORSTRUCT *mcolors)
  574. {
  575.     /* make sure help is not active */
  576.     if(_PL_helpfunc != NULL)
  577.         helpclose();
  578.  
  579.     hlpfile = filename;
  580.     hlpcolors = hcolors;
  581.     msgcolors = mcolors;
  582.  
  583.     _PL_helpfunc = helprun;
  584.  
  585.     stack_count = 0;   /* clear topic history */
  586.  
  587. } /* helpopen */
  588.