home *** CD-ROM | disk | FTP | other *** search
/ Nebula 1994 June / NEBULA_SE.ISO / SourceCode / OOP_Course / Examples / HelpEx / Help.m < prev    next >
Encoding:
Text File  |  1992-09-06  |  8.7 KB  |  304 lines

  1. /* 
  2.  * NeXTSTEP 3.0 mods - This was simply rebuilt with Project Builder.
  3.  * Of course, there is always the new 3.0 Help system, but if you want
  4.  * to try this simple approach, it still works.
  5.  *
  6.  *
  7.  * Author: Julie Zelenski, NeXT Developer Support
  8.  * (This is a simpler, reusable version of the one by Julie in
  9.  * NextDeveloper/Examples/BusyBox.  This one leaves out the context-
  10.  * sensitive help.  --  John Glover, University of Houston, glover@uh.edu)
  11.  *
  12.  * You may freely copy, distribute and reuse the code in this example.  
  13.  * NeXT disclaims any warranty of any kind, expressed or implied, as to 
  14.  * its fitness for any particular use.
  15.  *
  16.  * Help.m, a help object to manage and display RTF help files.
  17.  * The help object owns its own nib section "Help.nib" which has a
  18.  * Help panel - with an NXBrowser to display the help topics, and
  19.  * a scrolling text view to display the help files.  The help files are
  20.  * all stored as RTF text files in a directory called Help within the
  21.  * app wrapper.  At init time, the Help object loads the browser with 
  22.  * names of all the files found in the Help directory.  When a name is
  23.  * chosen from the browser, the help object opens a stream on that file
  24.  * and read the rich text into the text object.   This object is a useful 
  25.  * addition to any program, because all users appreciate help!
  26.  *
  27.  * To use this:
  28.  *    1.  Copy  Help.m, Help.h, and Help.nib to your project directory and add
  29.  *        them to the project.
  30.  *    2.  In the main .nib file, create Help as a new subclass of Object and parse it.
  31.  *    3.  Instantiate a Help object in the main .nib file.
  32.  *    4.  Connect your Help... menu item to the generalHelp: method of the
  33.  *        help instance.
  34.  *    5.  Connect your Print... menu item to the print: method of the
  35.  *        help instance.
  36.  *    6.  Modify the text at the top of the Help.nib panel to suit your application.
  37.  *    7.  Build your application as an .app, and create a Help folder within the .app folder.
  38.  *        In that folder, all .rtf filenames will show up as help topics.
  39.  *
  40.  */
  41.  
  42. // #import  <stdio.h>
  43. #import "Help.h"
  44. /* #import <appkit/Button.h>
  45. #import <appkit/Cell.h>
  46. #import <appkit/Matrix.h>
  47. #import <appkit/MenuCell.h>
  48. #import <appkit/NXBrowser.h>
  49. #import <appkit/NXBrowserCell.h>
  50. #import <appkit/ScrollView.h> 
  51. #import <appkit/appkit.h>*/
  52. //#import <dpsclient/wraps.h>
  53. #import <sys/dir.h> //for getdirentries()
  54. //#import <libc.h>     
  55. //#import  <appkit/Application.h>
  56. //#import  <appkit/defaults.h>  // for NXArgv
  57.  
  58. @implementation Help:Object
  59.  
  60.  
  61. - init 
  62. /* For newly created help object, loads the nib section with the Help
  63.  * panel which has the topics browser and a scrolling text view for 
  64.  * displaying help files.  Gets the appDirectory from NXApp, finds help 
  65.  * directory in app wrapper.
  66.  */
  67. {
  68.     sprintf(helpDirectory,"%s/%s",[self appDirectory],"Help");
  69.      helpPanel = [NXApp loadNibSection:"Help.nib" owner:self];
  70.     return self;
  71. }
  72.  
  73. /* Copied this from BusyBoxApp  with  minor changes.
  74.  * I have combined the two methods used there (new and 
  75.  * appDirectory) into one.
  76.  */
  77. - (const char *)appDirectory
  78. {
  79.     char *suffix;
  80.     
  81.     strcpy(appDirectory, NXArgv[0]);
  82.     if (suffix = rindex(appDirectory,'/'))
  83.         *suffix = '\0';
  84.     if (appDirectory) chdir(appDirectory);
  85.     getwd(appDirectory);
  86.     return  appDirectory;
  87. }
  88.     
  89. - setHelpBrowser:anObject;
  90. /* Sets the helpBrowser outlet, and calls on the browser to load up.
  91.  */
  92. {
  93.     helpBrowser = anObject;
  94.     [helpBrowser setDelegate:self];
  95.     [helpBrowser loadColumnZero];
  96.     return self;
  97. }
  98.  
  99. /* TARGET/ACTION METHODS */
  100.  
  101. - generalHelp:sender;
  102. /* This is the target/action method for the "Help" menu item.  This method 
  103.  * will show the "general help" file.
  104.  */
  105. {
  106.     [self showHelpFile:"General Help"];
  107.     return self;
  108. }
  109.  
  110. - browserHit:sender
  111. /* This is the target/action method from the help topics browser.  When
  112.  * a help topic is selected, this method will show the help file for that
  113.  * topic.
  114.  */
  115. {   
  116.     [self showHelpFile:[[[sender matrixInColumn:0] selectedCell] stringValue]];
  117.     return self;
  118. }
  119.  
  120.  
  121. - print:sender;
  122. /* This method is called by the Print menu cell in the main menu.  It will 
  123.  * print the current help file.
  124.  */
  125. {
  126.     [[helpScrollView docView] printPSCode:sender];
  127.     return self;
  128. }
  129.  
  130.  
  131. /* HELP METHODS */
  132.  
  133. - showHelpFile:(const char*)filename;
  134. /* Tries to open a stream for the specified RTF text file in the Help 
  135.  * directory so the text object can readRichText.  Also selects the
  136.  * filename in the browser of help topics. It also brings the
  137.  * help panel to the front.
  138.  */
  139. {
  140.    NXStream *stream;
  141.    char helpFile[MAXPATHLEN];
  142.    static NXPoint origin = {0.0,0.0};
  143.  
  144.     [self browser:helpBrowser selectCell:filename inColumn:0];
  145.     sprintf(helpFile,"%s/%s.rtf",helpDirectory,filename);
  146.     stream = NXMapFile(helpFile,NX_READONLY);
  147.     if (stream != NULL) {
  148.         [helpPanel disableFlushWindow];
  149.         [[helpScrollView docView] readRichText:stream]; 
  150.     [[helpScrollView docView] scrollPoint:&origin];
  151.     [[helpPanel reenableFlushWindow] flushWindow];
  152.         NXCloseMemory(stream,NX_FREEBUFFER);
  153.     }
  154.     [helpPanel orderFront:self];
  155.     return self;
  156. }
  157.  
  158.  
  159. /* BROWSER DELEGATE METHODS */
  160.  
  161.  
  162. #define CHUNK 127
  163. static char **addFile(const char *file, int length, char **list, int count)
  164. /* Adds the specified filename to the list of filenames.  It allocates 
  165.  * more memory in chunks as needed.
  166.  */
  167. {
  168.     char *suffix;
  169.     
  170.     if (!list) list = (char **)malloc(CHUNK*sizeof(char *));
  171.     if (suffix = rindex(file,'.')) 
  172.         *suffix  = '\0';     /* strip rtf suffix */
  173.     list[count] = (char *)malloc((length+1)*sizeof(char));
  174.     strcpy(list[count], file);
  175.     count++;
  176.     if (!(count% CHUNK)) {
  177.     list = (char **)realloc(list,(((count/CHUNK)+1)*CHUNK)*sizeof(char *));
  178.     }
  179.     list[count] = NULL;
  180.     return list;
  181. }
  182.  
  183. static void freeList(char **list)
  184. /* Frees the array of filenames
  185.  */
  186.  {
  187.     char **strings;
  188.  
  189.     if (list) {
  190.     strings = list;
  191.     while (*strings) free(*strings++);
  192.     free(list);
  193.     }
  194. }
  195.  
  196. static BOOL isOk(const char *s)
  197. /* checks to make sure the filename is not NULL and to verify that it is
  198.  * not a "dot"--hidden file.
  199.  */
  200. {
  201.     return (!s[0] || s[0] == '.') ? NO : YES;
  202. }
  203.  
  204. static int caseInsensitiveCompare(void *arg1, void *arg2)
  205. /* Compares the two arguments without regard for case using strcasecmp().
  206. */
  207. {
  208.     char *string1, *string2;
  209.  
  210.     string1 = *((char **)arg1);
  211.     string2 = *((char **)arg2);
  212.     return strcasecmp(string1,string2);
  213. }
  214.  
  215. static char **fileList;
  216.  
  217. - (int)browser:sender fillMatrix:matrix inColumn:(int)column
  218. /* This delegate method goes out to the help directory and gets a list
  219.  * of all the files in that directory.  It creates a list of file names
  220.  * for the static variable fileList, and will load the filenames into the 
  221.  * browser on demand (lazy loading).
  222.  */
  223. {
  224.     long basep;
  225.     char *buf;
  226.     struct direct *dp;
  227.     char **list = NULL;
  228.     int cc, fd, fileCount = 0;
  229.     char dirbuf[8192];
  230.  
  231.     if ((fd = open(helpDirectory, O_RDONLY, 0644)) > 0) {
  232.     cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
  233.     while (cc) {
  234.         dp = (struct direct *)buf;
  235.         if (isOk(dp->d_name)) {
  236.         list = addFile(dp->d_name, dp->d_namlen, list, fileCount++);
  237.         }
  238.         buf += dp->d_reclen;
  239.         if (buf >= dirbuf + cc) {
  240.         cc = getdirentries(fd, (buf = dirbuf), 8192, &basep);
  241.         }
  242.     }
  243.     close(fd);
  244.     if (list) qsort(list,fileCount,sizeof(char *),caseInsensitiveCompare);
  245.     }
  246.     freeList(fileList);
  247.     fileList = list;
  248.     return fileCount;
  249. }
  250.  
  251. - browser:sender loadCell:cell atRow:(int)row inColumn:(int)column
  252. /* This delegate method loads the cell for a given row.  The stringValue
  253.  * for that row comes from the fileList.
  254.  */
  255. {
  256.     if (fileList) {
  257.     [cell setStringValueNoCopy:fileList[row]];
  258.     [cell setLeaf:YES];
  259.     }
  260.     return self;
  261. }
  262.  
  263.  
  264. - (BOOL)browser:sender selectCell:(const char *)title inColumn:(int)column
  265. /* This delegate method selects the cell with the given title.  If it finds
  266.  * a cell with that title, it verifies that it has a file entry in the 
  267.  * fileList, forces the loading of the cell, selects it (highlights) and
  268.  * scrolls the browser so the cell is visible.  It returns a boolean value
  269.  * which indicates whether the cell was found.
  270.  */
  271. {
  272.     int row;
  273.     id matrix;
  274.  
  275.     if (title) {
  276.     matrix = [sender matrixInColumn:column];
  277.     if (!fileList) return NO;
  278.     for (row = [matrix cellCount]-1; row >= 0; row--) {
  279.         if (fileList[row] && !strcmp(title, fileList[row])) {
  280.         [sender getLoadedCellAtRow:row inColumn:column];
  281.         [matrix selectCellAt:row :0];
  282.         [matrix scrollCellToVisible:row :0];
  283.         return YES;
  284.         }
  285.     }
  286.     }
  287.     return NO;
  288. }
  289.  
  290.  
  291. /* WINDOW DELEGATE METHODS */
  292.  
  293. - windowWillResize:sender toSize:(NXSize *)frameSize;
  294. /* This method constrains the Help Panel to a reasonable minimum size
  295.  * when the user resizes the panel.
  296.  */
  297. {
  298.     frameSize->width = MAX(frameSize->width,400.0);
  299.     frameSize->height = MAX(frameSize->height,350.0);
  300.     return self;
  301. }
  302.  
  303.  
  304. @end