home *** CD-ROM | disk | FTP | other *** search
/ Education Sampler 1992 [NeXTSTEP] / Education_1992_Sampler.iso / Programming / Source / Gopher_1.12 / GopherClient.m < prev    next >
Encoding:
Text File  |  1992-03-18  |  18.6 KB  |  746 lines

  1. #import "GopherClient.h"
  2. #import "StuartSpeaker.h"
  3. #import "GopherDispatcher.h"
  4. #import <appkit/Listener.h>
  5. #import <appkit/Speaker.h>
  6. #import "PhSpeaker.h"
  7.  
  8. #include <stdio.h>
  9.  
  10. // Declared in GopherDispatcher.m
  11. extern char defaultHost[256];
  12. extern int defaultPort;
  13.  
  14. static int isIndexedText = NO;
  15. static char lastIndexWord[256];
  16.  
  17. @implementation GopherClient
  18.  
  19. ///////////////////////////////////////////////////
  20. //
  21.  
  22. - (char *)currentCellName
  23. {
  24.     id CurrentCell;
  25.  
  26.     CurrentCell = [[MainScroller matrixInColumn:[MainScroller selectedColumn]] 
  27.                    selectedCell];
  28.     return  (char *)[CurrentCell stringValue];
  29. }
  30.  
  31. //////////////////////////////////////////////////
  32. // Compute a new location for a window.
  33.  
  34. static void newLocation(NXPoint *p)
  35. {
  36.     static count = 0;
  37.  
  38.     p->x += (20.0 * count);
  39.     p->y -= (25.0 * count);
  40.     count = (count > 10)? 0 : count+1;
  41. }
  42.  
  43. //////////////////////////////////////////////////
  44. // Automatically called by the system when an instance is created.
  45.  
  46. - init
  47. {
  48.     NXRect     theFrame;
  49.  
  50.     [super init];
  51.  
  52.     strncpy(currenthostname, defaultHost, 250);
  53.     strcpy(currentfilename, "");  
  54.     currentport = defaultPort;
  55.  
  56.     [NXApp loadNibSection:"GopherClient.nib" owner:self];
  57.     //[myWindow setDelegate:self];
  58.     [myWindow getFrame:&theFrame];
  59.     newLocation(&theFrame.origin);
  60.     [myWindow moveTo:theFrame.origin.x :theFrame.origin.y];
  61.     [myWindow makeKeyAndOrderFront:nil];
  62.  
  63.     [[MainText docView] setAutodisplay:YES];
  64.  
  65.     [MainScroller setCellClass:[GopherBrowserCell class]];
  66.     [MainScroller useScrollButtons:YES];
  67.     [MainScroller loadColumnZero];
  68.  
  69.     theText = (char *)malloc(MAXTEXTLENGTH);
  70.  
  71.     return self;
  72. }
  73.  
  74. /////////////////////////////////////////////////
  75. // Close the window.
  76.  
  77. - close
  78. {
  79.     [myWindow performClose:self];
  80.     [self clean];
  81.     [myDispatcher oldFrontWindow:self];        // Theoretically unnecessary
  82.  
  83.     return self;
  84. }
  85.  
  86. /////////////////////////////////////////////////
  87. // Clean things up.
  88.  
  89. - clean
  90. {
  91.     [ItemInfoPanel performClose:self];
  92.     return self;
  93. }
  94.  
  95. /////////////////////////////////////////////////
  96. // Target method for the browser.
  97.  
  98. - cellClicked:sender
  99. {
  100.     id         CurrentCell;
  101.     int        remotePort;
  102.     ThreadArgument *theArg;
  103.     extern int    defaultApp;
  104.     char        *remotePath;
  105.  
  106. // Stuff borrowed from Rex Pruess's Remotes program
  107.     id            stuart;
  108.     int           code, ind;
  109.     char            *command;
  110.     char          *comArgs[10];
  111.     int           pid;
  112.     char          portStr[16];
  113.     char          loginStr[256];
  114.     char          errMsg[256];
  115.     extern char   **environ;     /* User environment */
  116. #ifdef DEBUG
  117.     char              **tempPtr;
  118. #endif
  119.  
  120. // Stuff for Rex Pruess's Ph CSO Nameserver client
  121.    int            flag;
  122.    int            msgDelivered;
  123.    char           server[128];
  124.    char           site[128];
  125.  
  126.  
  127.  
  128.     CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell];
  129.  
  130.     [ItemInfoHostName setStringValue:[CurrentCell remoteHost]];
  131.     [ItemInfoFileName setStringValue:[CurrentCell remoteName]];
  132.     [ItemInfoPortNumber setIntValue:[CurrentCell remotePort]];
  133.  
  134.     switch ([CurrentCell cellType]) {
  135.     case '0' :             // Document : retrieve and display
  136.         strncpy(currenthostname, [CurrentCell remoteHost], 255);
  137.         strcpy(currentfilename, [CurrentCell remoteName]);
  138.         currentport = [CurrentCell remotePort];
  139.         [ItemInfoType setStringValue:"ASCII text"];
  140.         [self displayDocument];
  141.         break;
  142.     case '1' :            // Directory : do nothing, taken care of in 
  143.                     // browser:fillMatrix:inColumn:
  144.         isIndexedText = NO;
  145.         [ItemInfoType setStringValue:"Directory"];
  146.         [[MainText docView] setText:""];
  147.         break;
  148.     case '2':                       // CSO Nameserver
  149.          if (![self createPort:self])
  150.                  break;
  151.  
  152.              strncpy (server, [CurrentCell remoteHost], 128);
  153.              strncpy (site, [CurrentCell stringValue], 128);
  154.  
  155.              msgDelivered = [mySpeaker showServer:server site:site ok:&flag];
  156.  
  157.              if (msgDelivered != 0) 
  158.                 fprintf (stderr, "Sorry, showServer message not delivered.\n");
  159.              else
  160.                 if (!flag) {
  161.                 fprintf (stderr, "\n*** showServer call failed. ***\n");
  162.                 fprintf (stderr, "*** Should only happen if 'server' is empty or too long.\n");
  163.                 }
  164.    
  165.              free (mySpeaker);
  166.          [ItemInfoType setStringValue:"Phone Server"];
  167.          break;
  168.     case '7' :            // Index search
  169.         [ItemInfoType setStringValue:"Index"];
  170.         isIndexedText = YES;
  171.         break;
  172.     case 'R':
  173.     case 'T':
  174.     case '8':            // Telnet session.  Revised by David Lacey        
  175.         [ItemInfoType setStringValue:"Telnet Session"];
  176.         isIndexedText = NO;
  177.         [[MainText docView] setText:""];
  178.         if ([CurrentCell remotePort] == 0)       // Seems common, so fix it
  179.         remotePort = 23;
  180.         else
  181.         remotePort = [CurrentCell remotePort];
  182.  
  183.             // If there is a login ID specified and we're telnetting, alert the user
  184.             if (([CurrentCell cellType] == '8') && 
  185.              *(remotePath = [CurrentCell remoteName])) {
  186.             sprintf (errMsg, "Login to remote system as '%s'.", remotePath);
  187.             NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL);
  188.                 }
  189.  
  190.         if (defaultApp == 0) { // Stewart or Terminal?
  191.         ind = 0;            
  192.             command = "/NextApps/Terminal";
  193.         comArgs[ind++] = "Terminal";
  194.  
  195.         comArgs[ind++] = "-Shell";
  196.  
  197.         switch ([CurrentCell cellType]) {
  198.                 case 'R':  // RLOGIN
  199.             strcpy (loginStr, "/usr/ucb/rlogin ");
  200.             if (*[CurrentCell remoteName] != '\0') {
  201.                   strcat (loginStr, " -l ");
  202.                            strcat (loginStr,  [CurrentCell remoteName]);
  203.                     }
  204.              strcat (loginStr, " ");
  205.             strcat (loginStr, [CurrentCell remoteHost]);
  206.             break;
  207.  
  208.             case '8':  // TELNET:
  209.             strcpy (loginStr, "/usr/ucb/telnet ");
  210.             strcat (loginStr, [CurrentCell remoteHost]);
  211.             if (remotePort != 23) {
  212.                 sprintf (portStr, " %d", remotePort);
  213.                 strcat (loginStr, portStr);
  214.             }
  215.              break;
  216.  
  217.             case 'T': // TN3270:
  218.             strcpy (loginStr, "/usr/ucb/tn3270 ");
  219.             strcat (loginStr, [CurrentCell remoteHost]);
  220.             break;
  221.  
  222.         }
  223.         comArgs[ind++] = loginStr;
  224.         comArgs[ind++] = NULL;
  225. #ifdef DEBUG
  226. /*              printf ("%s: %s",[NXApp appName], command);
  227.         tempPtr = comArgs;
  228.         while (*++tempPtr != NULL)
  229.             printf (" %s", *tempPtr);
  230.             printf ("\n");
  231.         }
  232. */
  233. #endif
  234.         pid = vfork ();
  235.          if (pid == 0) {
  236.             code = execve (command, comArgs, environ);
  237.             perror ([NXApp appName]);
  238.             fprintf (stderr, "%s: Execve error code=%d.\n",[NXApp appName], code);
  239.             _exit (0);
  240.         }
  241.  
  242.         } else {  // Send a message to Stuart
  243.  
  244.         stuart = [StuartSpeaker new];
  245.  
  246.         switch ([CurrentCell cellType]) {
  247.             case 'R':  // RLOGIN
  248.             strcpy (loginStr, "rlogin ");
  249.             if (*[CurrentCell remoteName] != '\0') {
  250.                 strcat (loginStr, " -l ");
  251.                            strcat (loginStr,  [CurrentCell remoteName]);
  252.                     }
  253.              strcat (loginStr, " ");
  254.             strcat (loginStr, [CurrentCell remoteHost]);
  255.                 break;
  256.  
  257.             case '8':  // TELNET:
  258.             strcpy (loginStr, "telnet ");
  259.             strcat (loginStr, [CurrentCell remoteHost]);
  260.             if (remotePort != 23) {
  261.                 sprintf (portStr, " %d", remotePort);
  262.                 strcat (loginStr, portStr);
  263.             }
  264.              break;
  265.  
  266.             case 'T': // TN3270:
  267.             strcpy (loginStr, "tn3270 ");
  268.             strcat (loginStr, [CurrentCell remoteHost]);
  269.             break;
  270.  
  271.         }
  272.         [stuart default:"Shell" as:loginStr];
  273.         [stuart default:"WriteStuartrc" as:"NO"];
  274.  
  275.         [stuart setActivate:TRUE];
  276.             
  277.         if ([stuart stuartConnectAndNew]) {
  278.             strcpy (errMsg, "There was a problem communicating with the Stuart ");
  279.             strcat (errMsg, "application.  (Speaker port unavailable.)  This may ");
  280.             strcat (errMsg, "have occurred because Stuart is not installed on ");
  281.             strcat (errMsg, "your system or because Stuart was slow in starting.");
  282.  
  283.             NXRunAlertPanel (NULL, errMsg, NULL, NULL, NULL, NULL);
  284.         }       
  285.             [stuart free];
  286.         }
  287.         
  288.         
  289.         break;
  290.  
  291.     case 's' :            // Sound
  292.         isIndexedText = NO;
  293.         [ItemInfoType setStringValue:"Sound"];
  294.         [[MainText docView] setText:""];
  295.         // Check that we're the only thread. We do it here because
  296.         // we can't have NXRunAlertPanel in a thread (why not ?)
  297.         if (mutex_try_lock(soundIsPlaying) == 0) {
  298.         NXRunAlertPanel("Gopher error", "Only one sound at a time !", "OK", NULL, NULL);
  299.         break;
  300.         }
  301.         
  302.         mutex_unlock(soundIsPlaying);
  303.         
  304.         soundShouldStopFlag = 0;
  305.         // The argument is malloc'ed because we may have several threads.
  306.         theArg = malloc(sizeof(ThreadArgument));
  307.         strncpy(theArg->hostname, [CurrentCell remoteHost], 255);
  308.         theArg->portnumber = [CurrentCell remotePort];
  309.         strncpy(theArg->filename, [CurrentCell remoteName], 1023);
  310.         cthread_detach(cthread_fork((cthread_fn_t)PlaySound, (any_t)theArg));
  311.         break;
  312.     default :
  313.         break;
  314.     }
  315.  
  316.     return self;
  317. }
  318.  
  319. ///////////////////////////////////////////////////////////////////
  320. // Highlight next occurance of search string, if it's indexed text.
  321. //
  322.  
  323. - highlightNextWord
  324. {
  325.      int findIndex, spaceIndex;
  326.      int minIndex = -1;
  327.      char *word, *cp = NULL, *textBuffer;
  328.      char wordStowage[256];
  329.      int done = 0;
  330.      NXSelPt begin, end;
  331.  
  332.      strcpy(wordStowage, lastIndexWord);
  333.      word = wordStowage;
  334.  
  335.      [[MainText docView] getSel:&begin :&end];
  336.  
  337.      textBuffer = theText + end.cp;
  338.  
  339.      while (!done) {
  340.  
  341.       cp = strchr(word, ' ');
  342.  
  343.       if (cp == NULL)
  344.            done = 1;
  345.       else
  346.            *cp='\0';
  347.  
  348. #ifdef DEBUG
  349.     fprintf(stderr, "Word is: %s\n", word);
  350. #endif
  351.  
  352.       // Don't highlight stupid words
  353.       
  354.       if ((strcasecmp(word, "and") != 0) &&
  355.           (strcasecmp(word, "or")  != 0) &&
  356.           (strcasecmp(word, "not") != 0) &&
  357.               (strcasecmp(word, "the") != 0))  {
  358.            
  359.  
  360.            findIndex = (int)strcasestr(textBuffer, word);
  361.  
  362.                if (findIndex == 0)
  363.                    ;        // Not found
  364.                else {
  365.                  if (minIndex == -1)
  366.                   minIndex = findIndex;
  367.                  else if (minIndex > findIndex) 
  368.                   minIndex = findIndex;
  369.            }
  370.         }
  371.  
  372.     word = cp +1;  // Move on to the next word.
  373.  
  374.      }
  375.      
  376.  
  377.      if (minIndex != -1) {
  378.  
  379.      spaceIndex = (int)index((char *)minIndex, ' ');
  380.  
  381.          if (spaceIndex > (int)index((char*)minIndex, '\r')) 
  382.         spaceIndex = (int)index((char*)minIndex, '\r');
  383.  
  384.          findIndex = (int)minIndex - (int)theText;
  385.          spaceIndex = (int)spaceIndex - (int)theText;
  386.  
  387.          // Highlight the found text, make it visible.
  388.  
  389.          [[MainText docView] setSel:findIndex :spaceIndex];
  390.          [[MainText docView] scrollSelToVisible];
  391.      }
  392.      else
  393.        NXBeep();
  394.      return self;
  395. }
  396.  
  397. ////////////////////////////////////////////////////////////////
  398. // Connect to currenthostname on port currentport, retrieve
  399. // document currentfilename and display it.
  400.  
  401. - displayDocument
  402. {
  403.     int iLength = 1, i;
  404.     int currentsocket;
  405.  
  406.     currentsocket = create_socket(currenthostname, currentport);
  407.     if (currentsocket < 0)
  408.     {
  409.     switch (currentsocket)
  410.         {
  411.         case -1 :
  412.         NXRunAlertPanel("Gopher error", "Unknown host", "OK", NULL, NULL);
  413.         return self;
  414.         break;
  415.         case -2 :
  416.          NXRunAlertPanel("Gopher error", "Socket call failed",                                                
  417.                         "OK", NULL, NULL);
  418.         return self;
  419.         break;
  420.           case -3 :
  421.         NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL);
  422.         return self;
  423.         break;
  424.         default :
  425.         break;
  426.         }
  427.     }
  428.  
  429.     writestring(currentsocket, currentfilename);
  430.     writestring(currentsocket, "\r\n");
  431.  
  432.     for (i=0; (i < MAXTEXTLENGTH) && iLength; i += iLength)
  433.     iLength = read(currentsocket, theText + i, 10000);
  434.     
  435.     theText[i - 3] = '\0';        // -3 to get rid of final period
  436.     [[MainText docView] setText:theText];
  437.  
  438.     close(currentsocket);
  439.  
  440.     [[MainText docView] setSel:0 :0];
  441.     [[MainText docView] scrollSelToVisible];
  442.  
  443.     if (isIndexedText)
  444.     [self highlightNextWord];
  445.  
  446.  
  447.  
  448. /*    findIndex = (int)strstr(theText, lastIndexWord);
  449.     if (findIndex == 0)
  450.         ;        // Not found
  451.     else {
  452.         spaceIndex = (int)index((char *)findIndex, ' ');
  453.         findIndex = (int)findIndex - (int)theText;
  454.         spaceIndex = (int)spaceIndex - (int)theText;
  455.         [[MainText docView] setSel:findIndex :spaceIndex];
  456.         [[MainText docView] scrollSelToVisible];
  457.     }
  458.     }
  459. */
  460.     return self;
  461. }
  462.  
  463. /////////////////////////////////////////////////
  464. // Find our dispatcher
  465.  
  466. - setDispatcher:sender
  467. {
  468.     myDispatcher = sender;
  469.     return self;
  470. }
  471.  
  472. /////////////////////////////////////////////////
  473. // Print the contents of the text window.
  474.  
  475. - print
  476. {
  477.     [[MainText docView] printPSCode:self];
  478.  
  479.     return self;
  480. }
  481.  
  482. /////////////////////////////////////////////////
  483. // Saves the contents of the text window.
  484.  
  485. - saveAs:(char *)filename
  486. {
  487.     NXStream *tmpstream;
  488.  
  489.     tmpstream = NXOpenMemory(NULL, 0, NX_WRITEONLY);
  490.     [[MainText docView] writeText:tmpstream];
  491.     NXSaveToFile(tmpstream, filename);
  492.     NXCloseMemory(tmpstream, NX_FREEBUFFER);
  493.  
  494.     return self;
  495. }
  496.  
  497. /////////////////////////////////////////////////
  498. // 
  499.  
  500. - searchIndex:sender
  501. {
  502.     [myIndexPanel orderOut:self];
  503.     [NXApp stopModal:1];
  504.  
  505.     return self;
  506. }
  507.  
  508. /////////////////////////////////////////////////
  509. // 
  510.  
  511. - cancelIndex:sender
  512. {
  513.     [myIndexPanel orderOut:self];
  514.     [NXApp stopModal:2];
  515.  
  516.     return self;
  517. }
  518.  
  519. /////////////////////////////////////////////////
  520. // 
  521.  
  522. - getIndexString:(char *)theString maxLength:(int)maxlength
  523. {
  524.     [myIndexPanel makeKeyAndOrderFront:self];
  525.     [IndexTextItem selectText:self];
  526.     if ([NXApp runModalFor:myIndexPanel] == 1)    // User clicked OK
  527.     strncpy(theString, [IndexTextItem stringValue], maxlength);
  528.     else
  529.     strcpy(theString, "");            // User clicked Cancel
  530.  
  531.     return self;
  532. }
  533.  
  534. /////////////////////////////////////////////////
  535. //
  536.  
  537. - showItemInfo
  538. {
  539.     [ItemInfoPanel makeKeyAndOrderFront:self];
  540.     return self;
  541. }
  542.  
  543. /////////////////////////////////////////////////
  544. // Delegate method for the browser
  545.  
  546. - (int)browser:sender fillMatrix:matrix inColumn:(int)column
  547. {
  548.     id        tmpcell;
  549.     int     iLength;
  550.     char     inputline[512];
  551.     char     *remotename, *remotehost, *remoteport;
  552.     int     currentsocket;
  553.     id         CurrentCell;
  554.  
  555.     CurrentCell = [[sender matrixInColumn:[sender selectedColumn]] selectedCell];
  556.  
  557.     switch ([CurrentCell cellType]) {
  558.     case '0' :             // Document : should never happen
  559.         break;
  560.     case '1' :            // Directory : the browser will
  561.                     // automatically update
  562.         strncpy(currenthostname, [CurrentCell remoteHost], 250);
  563.         strcpy(currentfilename, [CurrentCell remoteName]);
  564.         currentport = [CurrentCell remotePort];
  565.         break;
  566.     case '7' :            // Index search
  567.         strncpy(currenthostname, [CurrentCell remoteHost], 250);
  568.         [self getIndexString:&lastIndexWord maxLength:250];
  569.         if (strlen(lastIndexWord) == 0)        // User clicked Cancel
  570.         return 0;
  571.         strcpy(currentfilename, [CurrentCell remoteName]);
  572.             strcat(currentfilename, "\t");
  573.             strcat(currentfilename, lastIndexWord);
  574.         currentport = [CurrentCell remotePort];
  575.         break;
  576.     case '8' :            // Telnet session : should never happen
  577.         break;
  578.     case 's' :            // Sound : should never happen
  579.         break;
  580.     default :
  581.         break;
  582.     }
  583.  
  584.     currentsocket = create_socket(currenthostname, currentport);
  585.     if (currentsocket < 0) 
  586.         switch (currentsocket) {
  587.         case -1 :
  588.         NXRunAlertPanel("Gopher error", "Unknown host",    "OK", NULL, NULL);
  589.         return(0);
  590.         break;
  591.         case -2 :
  592.         NXRunAlertPanel("Gopher error", "Socket call failed", "OK", NULL, NULL);
  593.         return(0);
  594.         break;
  595.         case -3 :
  596.         NXRunAlertPanel("Gopher error", "Connect call failed", "OK", NULL, NULL);
  597.         return(0);
  598.         break;
  599.         default :
  600.         break;
  601.     }
  602.  
  603.     writestring(currentsocket, currentfilename);
  604.     writestring(currentsocket, "\r\n");
  605.  
  606.     for(;;) {
  607.     iLength = readline(currentsocket, inputline, 512);
  608.       
  609.     if (iLength == -1)
  610.         break;
  611.     if (iLength == 0)
  612.         break;
  613.  
  614.     ZapCRLF(inputline);
  615.       
  616.     if ((inputline[0] == '0') || (inputline[0] == '1') ||
  617.           (inputline[0] == '7') || (inputline[0] == '8') ||
  618.           (inputline[0] == 'R') || (inputline[0] == 'T') ||
  619.           (inputline[0] == 's') || (inputline[0] == '2')) {
  620.            if ((inputline[0] == 's') && (shouldUseSound == 0))
  621.         continue;
  622.  
  623.            [matrix addRow];
  624.         tmpcell = [matrix cellAt:([matrix cellCount] -1) :0];
  625.         
  626.         remotename = index(inputline, '\t') + 1;
  627.         *(remotename-1) = '\0';
  628.         
  629.         remotehost = index(remotename, '\t') + 1;
  630.         *(remotehost-1) = '\0';
  631.         
  632.         remoteport = index(remotehost, '\t') + 1;
  633.         *(remoteport-1) = '\0';
  634.  
  635.         [tmpcell setRemoteName:remotename];
  636.         [tmpcell setRemoteHost:remotehost];
  637.         [tmpcell setRemotePort:atoi(remoteport)];
  638.         [tmpcell setStringValue:(inputline+1)];
  639.         [tmpcell setLoaded:YES];
  640.         if (inputline[0] == '0') {        // Document
  641.         [tmpcell setLeaf:YES];
  642.         [tmpcell setCellType:'0'];
  643.         }
  644.         else if (inputline[0] == '1') {    // Directory
  645.             [tmpcell setLeaf:NO];
  646.         [tmpcell setCellType:'1'];
  647.         }
  648.         else if (inputline[0] == '2') {     // CSO Nameserver
  649.         [tmpcell setLeaf:YES];
  650.         [tmpcell setCellType:'2'];
  651.         }
  652.  
  653.         else if (inputline[0] == '7') {    // Index
  654.         [tmpcell setLeaf:NO];
  655.         [tmpcell setCellType:'7'];
  656.         }
  657.         else if (inputline[0] == '8') {    // Telnet
  658.         [tmpcell setLeaf:YES];
  659.         [tmpcell setCellType:'8'];
  660.         }
  661.         else if (inputline[0] == 'R') {    // Rlogin
  662.         [tmpcell setLeaf:YES];
  663.         [tmpcell setCellType:'R'];
  664.         }        
  665.         else if (inputline[0] == 'T') {    // TN3270
  666.         [tmpcell setLeaf:YES];
  667.         [tmpcell setCellType:'T'];
  668.         }        
  669.         else if (inputline[0] == 's') {    // Sound
  670.         [tmpcell setLeaf:YES];
  671.         [tmpcell setCellType:'s'];
  672.         }
  673.     }
  674.     
  675.     if ((inputline[0] == '.') && (inputline[1] == '\0'))
  676.         break;
  677.     }
  678.  
  679.     close(currentsocket);
  680.  
  681.     return([matrix cellCount]);
  682. }
  683.  
  684. ////////////////////////////////////////////////////////////////////////////
  685. // Delegate methods for the window
  686.  
  687. /////////////////////////////////////////////////
  688. // Called when our window becomes the key window.
  689.  
  690. - windowDidBecomeKey:sender
  691. {
  692.    [myDispatcher newFrontWindow:self];
  693.  
  694.    return self;
  695. }
  696.  
  697. /////////////////////////////////////////////////
  698. // Called when our window has stopped being the key window.
  699.  
  700. - windowDidResignKey:sender
  701. {
  702.     [myDispatcher oldFrontWindow:self];
  703.  
  704.     return self;
  705. }
  706.  
  707. /////////////////////////////////////////////////
  708. // Called when our window is going to close.
  709.  
  710. - windowWillClose:sender
  711. {
  712.     [self clean];
  713.     [myDispatcher oldFrontWindow:self];
  714.  
  715.     return self;
  716. }
  717.  
  718.  
  719. //  Stuff from Rex Pruess's Ph CSO Nameserver App
  720.  
  721. /*---------------------------------------------------------------------------
  722. This internal support method sets the Speaker port and creates the PhSpeaker
  723. object.
  724. -----------------------------------------------------------------------------*/
  725. - (BOOL) createPort:sender
  726. {
  727.    port_t         thePort;
  728.  
  729.    thePort = NXPortFromName ("Ph", NULL);
  730.    
  731.    if (thePort == PORT_NULL) {
  732.       fprintf (stderr, "\n*** PhSpeaker port is NULL.\n");
  733.       fprintf (stderr, "*** Perhaps Ph is not installed in a standard directory.\n");
  734.       return NO;
  735.    }
  736.  
  737.    mySpeaker = [[PhSpeaker alloc] init];
  738.  
  739.    [mySpeaker setSendPort:thePort];
  740.  
  741.    return YES;
  742. }
  743.  
  744.  
  745. @end
  746.