home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / AppKit / SortingInAction / SortApp.m < prev    next >
Encoding:
Text File  |  1992-06-16  |  9.2 KB  |  309 lines

  1.  
  2. /*
  3.  * This class is in charge of application-wide activity such as handling the
  4.  * main menu commands, enabling and disabling controls and managing the tick
  5.  * counter display.  It is also responsible for receiving drawing requests 
  6.  * from the sort threads.  When any thread needs to draw, it conjures up a 
  7.  * simple Mach message which it sends to the drawing port that the SortApp 
  8.  * monitors.  The SortApp will have the appropriate view do the necessary
  9.  * drawing.
  10.  *
  11.  * Author: Julie Zelenski, NeXT Developer Support
  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.  
  17. #import <appkit/appkit.h>
  18. #import "SortApp.h"
  19. #import "SortController.h"
  20. #import "GenericSort.h"
  21. #import "SortView.h"
  22. #import <mach/cthreads.h>
  23. #import <string.h>
  24.  
  25.  
  26. /* Global variables shared across several class/threads. */
  27.  
  28. BOOL Abort;            // global variable to signal abort 
  29. int TickCount;                  // global variable of current tick count 
  30. mutex_t TickLock;        // mutual exclusion to protect TickCount 
  31. condition_t TickUpdated;          // signaled when TickCount is increased 
  32.  
  33.  
  34. @implementation SortApp
  35.  
  36.  
  37. + initialize;
  38. /* +initialize is sent to the factory object for SortApp before any 
  39.  * other messages are sent.  This is a good place to allocate and initialize 
  40.  * the global variables.
  41.  */
  42. {
  43.     if (self == [SortApp class]) {
  44.     Abort = NO;
  45.     TickCount = 0;
  46.     TickLock = mutex_alloc();
  47.     TickUpdated = condition_alloc();
  48.     }
  49.     return self;
  50. }
  51.  
  52.  
  53. + new;
  54. {  
  55.     self = [super new];
  56.     teNum = (DPSTimedEntry)0;
  57.     return self;
  58. }
  59.  
  60.  
  61.  
  62. /* TARGET-ACTION METHODS */
  63.  
  64. - info:sender
  65. /*
  66.  * The info panel is a separate nib module and only loaded on demand.
  67.  */
  68. {
  69.     if (!infoPanel) {
  70.     if (![self loadNibSection:"InfoPanel.nib" owner:self withNames:NO]) {
  71.         NXLogError ("Could not load InfoPanel.nib");
  72.     }
  73.     }
  74.     [infoPanel makeKeyAndOrderFront:self];
  75.     return self;
  76. }
  77.  
  78.  
  79. - help:sender
  80. /*
  81.  * The help panel is a separate nib module and only loaded on demand.
  82.  */
  83. {
  84.     if (!helpPanel) {
  85.     if (![self loadNibSection:"HelpPanel.nib" owner:self withNames:NO]) {
  86.         NXLogError ("Could not load HelpPanel.nib");
  87.     }
  88.     }
  89.     [helpPanel makeKeyAndOrderFront:self];
  90.     return self;
  91. }
  92.  
  93.  
  94. - go:sender
  95. /*
  96.  * This button gets the whole thing going.  It disables the controls that 
  97.  * will be inactive while the sort is running, resets the tick counter, and 
  98.  * asks the SortController to start the trial.  The SortController will launch 
  99.  * a thread to generate the data and run the sorts, so this method will return 
  100.  * almost immediately.  That way the user interface of the program remains 
  101.  * responsive.  The user can stop the sort, change the speed, go look at the 
  102.  * Info panel, whatever!  When a sort is running, this button becomes the Stop 
  103.  * button, used to cancel a sort in progress.
  104.  */
  105. {
  106.     if (!strcmp([sender icon],"Stop")) // if sorting, this is "Stop" button
  107.         return [self stop:self];
  108.     if ([[goButton window] makeFirstResponder:[goButton window]]) {
  109.         if ([sortController startSort]) { // if trial got started ok
  110.         [self setControlsEnabled:NO];
  111.         [messageField setStringValue:
  112.                 [stringTable valueForStringKey:"Data"]];
  113.         [tickField setIntValue:0];
  114.     }
  115.     } 
  116.     return self;
  117. }
  118.  
  119.  
  120. - stop:sender
  121. /*
  122.  * This method cancels a sort in progress the whole thing going.  It stops 
  123.  * the tick counter and sets the global variable Abort to YES.  The sorting 
  124.  * threads continually check this variable and when the recognize it they 
  125.  * send one last message to the drawPort which will display the canceled 
  126.  * sort. When they have all check in, the method allFinished will do any 
  127.  * necessary clean up. 
  128.  */
  129. {
  130.     Abort = YES;    // threads will pick up on this & report back when killed
  131.     [messageField setStringValue:[stringTable valueForStringKey:"Canceled"]];
  132.     condition_broadcast(TickUpdated); 
  133.     [self stopTickCounter];
  134.     return self;
  135. }
  136.  
  137.  
  138. - setControlsEnabled:(BOOL)value;
  139. /* This method is called to disable all the inactive controls in the parameter 
  140.  * window while a sort is in progress (the data set size, percent sorted, etc).  
  141.  * This method is also called to enable those controls when the sorts have
  142.  * finished.
  143.  */
  144. {
  145.     [[performRadios window] disableFlushWindow];
  146.     [performRadios setEnabled:value];
  147.     [dataSetMatrix setEnabled:value];
  148.     [algorithmMatrix setEnabled:value];
  149.     [tickValueMatrix setEnabled:value];
  150.     [tickMatrix setEnabled:value];
  151.     if (value) 
  152.         [goButton setIcon:"Go!"];
  153.     else 
  154.         [goButton setIcon:"Stop"];
  155.     [[[performRadios window] reenableFlushWindow] flushWindow];
  156.     return self;
  157. }
  158.  
  159.  
  160. - allFinished;
  161. /* This method is sent by the SortController when it has verified that 
  162.  * all sorts have finished in the current trial.  It will stop the tick 
  163.  * counter, re-enable the controls, and reset Abort to NO.
  164.  */
  165. {
  166.     [self stopTickCounter];
  167.     [self setControlsEnabled:YES];
  168.     if (!Abort) 
  169.         [messageField setStringValue:[stringTable valueForStringKey:"Done"]];
  170.     Abort = NO;
  171.     return self;
  172. }
  173.  
  174.  
  175. - setTickField:anObject
  176. /*
  177.  * The tickField is a text field on the animation window that displays the
  178.  * tick count.  When a sort is running, a timed entry is called every second 
  179.  * to update the tick field.  The timed entry runs at a priority one higher
  180.  * than NXMODALRESPTHRESHOLD.  Because of this, the tick field is updated even 
  181.  * during a modal response loop (such as changing the speed slider).
  182.  */
  183.     tickField = anObject;     
  184.     [tickField allocateGState];    // for more efficient lock/unlock focus
  185.     return self; 
  186. }
  187.  
  188.  
  189. static void updateTick (teNum,now,self)
  190.   DPSTimedEntry teNum;
  191.   double now;
  192.   SortApp *self;
  193. {
  194.     mutex_lock(TickLock);
  195.     [self->tickField setIntValue:TickCount];    // display current tick count
  196.     mutex_unlock(TickLock);
  197. }
  198.  
  199.     
  200. - startTickCounter;
  201. /* This method is called to reset the tick count to zero and to kick off the 
  202.  * timed entry that will update the tick field display every second.
  203.  */
  204. {
  205.     TickCount = 0;
  206.     if (teNum) 
  207.         DPSRemoveTimedEntry(teNum);
  208.     teNum = DPSAddTimedEntry(1.0, &updateTick, self, NX_MODALRESPTHRESHOLD +1);
  209.     return self;
  210. }
  211.  
  212.  
  213. - stopTickCounter;
  214. /* This method is called to remove the timed entry that has been updating the 
  215.  * tick field every second.  The tick field will display the current tick 
  216.  * count.
  217.  */
  218. {
  219.     if (teNum) 
  220.         DPSRemoveTimedEntry(teNum);
  221.     teNum = (DPSTimedEntry)0;
  222.     [tickField setIntValue:TickCount];
  223.     return self;
  224. }
  225.  
  226.  
  227. - stringTable;
  228. /* This method simply returns the string table for other objects which need to 
  229.  * access strings.
  230.  */
  231. {
  232.     return stringTable;
  233. }
  234.  
  235. - draw:(simpleMsg *)msg;
  236. /* This is the method called by the message handler to do draw requests from 
  237.  * the sorting threads.  The messages are configured so that the msg_id field 
  238.  * of the message header indicates what type of message it is (swap, move, 
  239.  * compare, or finished).  The first integer in the message is the sortNum 
  240.  * which is the unique integer for the sort sending the message. This integer 
  241.  * can be passed to the SortController to get the Sort object for that sort, 
  242.  * which can be used to get its SortView.   The other integers passed in the 
  243.  * message are the parameters for the different draw messages (the values to 
  244.  * swap, the positions to compare, etc).  The SortApp then messages the 
  245.  * appropriate SortView, passing the parameters, and the SortView will do the 
  246.  * drawing.
  247.  */
  248. {
  249.     id view;
  250.     
  251.     view = [[sortController findSortWithNum:msg->sortNum] view];
  252.     switch(msg->h.msg_id) {
  253.         case SORTING_MSG:   if (!Abort) [messageField setStringValue:
  254.                     [stringTable valueForStringKey:"Sorting"]];
  255.                     break;
  256.     case COMPARE_MSG:   [view compare:msg->position[0] value:msg->value[0]
  257.                 with:msg->position[1] value:msg->value[1]];
  258.                 break;
  259.         case SWAP_MSG:      [view swap:msg->position[0] value:msg->value[0]
  260.                 with:msg->position[1] value:msg->value[1]];
  261.                       break;
  262.         case MOVE_MSG:      [view moveValue:msg->value[0] to:msg->position[0] 
  263.                 oldValue:msg->value[1]];
  264.                        break;
  265.         case FINISHED_MSG:  [view displayFinished];
  266.                     [sortController sortFinished:msg->sortNum];
  267.                 break;
  268.     }
  269.     return self;
  270. }
  271.  
  272. static void msgHandler(msg,self)
  273.   simpleMsg *msg;
  274.   id self;
  275. {    
  276.     [self draw:msg];
  277. }
  278.  
  279.  
  280. - (port_t)drawPort;
  281. /* This method returns the port allocated by the main thread.  All drawing 
  282.  * messages are sent to this port. 
  283.  */
  284. {
  285.    return drawPort;
  286. }
  287.  
  288.  
  289. /* DELEGATE METHODS */
  290.  
  291. - appDidInit:sender
  292. /* This method allocates the port for the main thread where all drawing 
  293.  * messages are sent.  Then, it adds the port to those monitored by the 
  294.  * application.  Whenever a message is sent to this port, the handler 
  295.  * function is called to draw the message.  This port is registered at a 
  296.  * priority of one greater than NX_MODAL RESPTHRESHOLD.  Because of this, 
  297.  * the drawing messages will continue to be processed, even during a modal 
  298.  * response loop (such as changing the speed slider).  The speed slider sends 
  299.  * message continuously so you can dynamically change the speed and immediately 
  300.  * see the effects of those changes.
  301.  */
  302. {      
  303.     port_allocate(task_self(), &drawPort);
  304.     DPSAddPort(drawPort,msgHandler,sizeof(simpleMsg),self,NX_MODALRESPTHRESHOLD +1);
  305.     return self;
  306. }
  307.  
  308. @end