home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.0 / NeXTSTEP3.0.iso / NextDeveloper / Examples / AppKit / SortingInAction / SortController.m < prev    next >
Encoding:
Text File  |  1992-05-27  |  11.1 KB  |  379 lines

  1.  
  2. /* The SortController class is the "brains" of the sorting operations.  As the 
  3.  * shepherd of a flock of sorts, it keeps an array of Sort objects.  The 
  4.  * SortController starts off the sort trials when the SortApp gets the "go!" 
  5.  * signal.  The SortController generates the data and forks off the sort 
  6.  * threads.  It keeps track of when the sorts finish, starts the next sort 
  7.  * when the sorts are running in series, and lets the SortApp know when all 
  8.  * the sorts have finished. Most of the controls of the Parameters panel send 
  9.  * their target-action methods to the SortController and it sends the changes 
  10.  * on to the sorts as necessary.  The SortController is also the text delegate 
  11.  * for the various text fields and forms of the Parameters panel.  It does 
  12.  * entry checking to make sure the user enters reasonable values.
  13.  *
  14.  * Author: Julie Zelenski, NeXT Developer Support
  15.  * You may freely copy, distribute and reuse the code in this example.  
  16.  * NeXT disclaims any warranty of any kind, expressed or implied, as to 
  17.  * its fitness for any particular use.
  18.  */
  19.  
  20. #import <appkit/appkit.h>
  21. #import "SortController.h"
  22. #import "BubbleSort.h"
  23. #import "InsertionSort.h"
  24. #import "MergeSort.h"
  25. #import "QuickSort.h"
  26. #import "SelectionSort.h"
  27. #import "ShellSort.h"
  28. #import "SortApp.h"
  29. #import "SortView.h"
  30. #imporUYibc.h>
  31. #import <math.h>
  32. #import <mach/cthreads.h>
  33.  
  34.  
  35. extern BOOL Abort;        // global variable to signal abort 
  36.  
  37.  
  38. @implementation SortController:Object
  39.  
  40.  
  41. - init;
  42. /* The initialization method is called once, when unarchiving the nib section.  
  43.  * There is only one instantiation of the SortConroller object. This method 
  44.  * creates a new sort object for each algorithm and initializes some instance
  45.  * variables.
  46.  */
  47. {  
  48.     [super init];
  49.     srandom(time(0));             // Seed random for data generating
  50.                         // Create on of each sort object
  51.     sort[BUBBLE_SORT] = [[BubbleSort allocFromZone:[self zone]] init];
  52.     sort[INSERTION_SORT] = [[InsertionSort allocFromZone:[self zone]] init];
  53.     sort[MERGE_SORT] = [[MergeSort allocFromZone:[self zone]] init];
  54.     sort[QUICK_SORT] = [[QuickSort allocFromZone:[self zone]] init];
  55.     sort[SELECTION_SORT] = [[SelectionSort allocFromZone:[self zone]] init];
  56.     sort[SHELL_SORT] = [[ShellSort allocFromZone:[self zone]] init];
  57.     sortOn[QUICK_SORT] =  sortOn[INSERTION_SORT] = sortOn[SHELL_SORT] = YES;
  58.     sortOn[BUBBLE_SORT] = sortOn[SELECTION_SORT] = sortOn[MERGE_SORT] = NO;
  59.     numSorts = 3;
  60.     parallel = YES;
  61.     numElements = 50;
  62.     percentSorted = 0;
  63.     return self;
  64. }
  65.  
  66. #define SIZE 1        // tags for different text fields
  67. #define PERCENT 2
  68. #define COMPARE 3
  69. #define MOVE 4
  70. #define FCALL 5    
  71.  
  72.  
  73. /* TEXT DELEGATE METHODS */
  74.  
  75.  
  76. - (BOOL)textWillEnd:textObject;
  77. /* Checks to make sure that the entry in each of the text fields is reasonable. 
  78.  * Different values are reasonable based on what the field is.  You can get the 
  79.  * field or form being edited by asking the text object for its delegate.  If 
  80.  * the value is unreasonable, reject it, and set the value back to the closest
  81.  * reasonable value. Then it sends the action to the target so that change is 
  82.  * recorded.
  83.  */
  84. {   
  85.     BOOL entryOK = YES;
  86.     id form;
  87.     int value;
  88.     
  89.     form = [textObject delegate];
  90.     value = [[form selectedCell] intValue];
  91.     
  92.     switch ([form selectedTag]) {
  93.           case SIZE:     entryOK = (value > 0 );
  94.             if (value>5000) [[form selectedCell] setIntValue:5000];
  95.             break;
  96.         case PERCENT:   entryOK = ((value >= 0) && (value <= 100));
  97.             break;
  98.       case FCALL:       
  99.     case MOVE:     
  100.       case COMPARE:     entryOK = (value >= 0);
  101.             break;
  102.     }
  103.     if (entryOK) [form sendAction:[form action] to:[form target]]U`  return (!entryOK);
  104. }
  105.  
  106.  
  107.  
  108. /*  TARGET-ACTION METHODS (for controls in the Sort Parameters panel) */
  109.  
  110. - changeAnimate:sender
  111. /* Changes whether the sorts highlight their comparisions. SortController 
  112.  * simply passes this method onto each of the sorts.
  113.  */
  114. {   int c;
  115.     BOOL value = (BOOL)[sender state];
  116.     
  117.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  118.         [sort[c] setAnimate:value];
  119.     return self;
  120. }
  121.  
  122.  
  123. - changeParallel:sender
  124. /* Changes whether the sorts run in parallel or series. SortController needs 
  125.  * this to know whether to launch all sort threads at once, or to wait until 
  126.  * one has finished to start the next sort.
  127.  */
  128. {
  129.     parallel = (BOOL)[sender selectedTag];
  130.     return self;
  131. }
  132.  
  133.  
  134. - changeSpeed:sender;
  135. /* Changes at what speed the sorts are running.  SortController simply passes 
  136.  * this method onto each of the sorts.
  137.  */
  138. {
  139.     int c, speed;
  140.     speed = [(Slider *)sender maxValue] - [sender intValue];
  141.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  142.        [sort[c] setSpeed:speed];
  143.     return self;
  144. }
  145.  
  146.  
  147. - changeSortOn:sender
  148. /* Changes whether a selected sort is set to run or not.  Increments or 
  149.  * decrements the number of sorts in this trial as appropriate.
  150.  */
  151. {   BOOL on; 
  152.  
  153.     on = (BOOL)[[sender selectedCell] state];
  154.     sortOn[[sender selectedTag]] = on;
  155.     if (on) 
  156.         numSorts++;
  157.     else 
  158.         numSorts--;
  159.     return self;
  160. }
  161.  
  162.  
  163.       
  164. - changeDataSet:sender;
  165. /* Changes the size or percent sorted of a data set, uses tag to determine 
  166.  * which.
  167.  */
  168. {
  169.     switch ([sender selectedTag]) {
  170.         case SIZE:    numElements = [sender intValue];
  171.             break;
  172.         case PERCENT:   percentSorted = [sender intValue];
  173.             break;
  174.     }
  175.     return self;
  176. }
  177.  
  178.  
  179. - changeTickValue:sender;
  180. /* Changes the tick cost of a certain operation, uses tag to determine which.  
  181.  * SortController simply passes this method onto each of the sorts.
  182.  */
  183. {
  184.     int c, value;
  185.     
  186.     value = [sender intValue];
  187.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  188.         [sort[c] setTicks:value for:[sender selectedTag]];
  189.     return self;
  190. }
  191.  
  192.  
  193.  
  194.  
  195. /* PRIVATE METHODS */
  196.  
  197.  
  198. - setSortWindow:anObject
  199. /* The sortWindow is where all the animation takes place.
  200.  */
  201. {   
  202.     sortWindow = anObject;  
  203.     [[sortWindow contentView] setFlipped:YES];     
  204.     return self;
  205. }
  206.  
  207.  
  208. - setUpWindow;
  209. /* This is the method that adjusts the sorting window for a new trial.  It 
  210.  Uasizes the window to fit all the needed sort views and then adds those 
  211.  * sortviews as subviews of the content view.  It removes any sortviews of
  212.  * sorts that aren't scheduled to run in this trial.
  213.  */
  214. {
  215.     NXPoint pt = {2.0,2.0};
  216.     NXRect windowRect,headerRect;
  217.     float newHeight;
  218.     int c;
  219.     
  220.     [sortWindow disableFlushWindow];
  221.     [sortWindow getFrame:&windowRect];
  222.     [windowHeader getFrame:&headerRect];
  223.     newHeight = headerRect.size.height + numSorts*(VIEW_HEIGHT+2) + 24;
  224.     windowRect.origin.y += windowRect.size.height - newHeight;
  225.     windowRect.size.height = newHeight;
  226.     [sortWindow placeWindow:&windowRect];
  227.     [windowHeader moveTo:headerRect.origin.x :-3];
  228.     pt.y = headerRect.size.height;
  229.     for (c = 0; (c < NUM_SORT_TYPES); c++) {
  230.         if (sortOn[c]) {
  231.         [[sort[c] view] moveTo:pt.x :pt.y];
  232.         [[sortWindow contentView] addSubview:[sort[c] view]];
  233.         pt.y += VIEW_HEIGHT+2.0;
  234.     } else {
  235.         [[sort[c] view] removeFromSuperview]; 
  236.     }
  237.      }   
  238.      [sortWindow display];
  239.      [[[sortWindow reenableFlushWindow] flushWindow] orderFront:self];
  240.      return self;
  241. }
  242.  
  243.  
  244. - (int *)newDataSet
  245. /* This is the method to generate a new data set.  Basically, it cycles 
  246.  * through a loop numElements times, getting a new number for each position 
  247.  * of the array.  For completely random data, each element is randomly 
  248.  * generated.  For partially sorted data, only some of the elements are random, 
  249.  * others are inserted in sorted position.  Each time through the loop, a 
  250.  * random number is first generated to determine whether this array element 
  251.  * will be random. If the random number is greater than the percent sorted, 
  252.  * it will generate a random number for that array element, otherwise it will 
  253.  * assign an element in sorted order to that array element. */
  254. {    
  255.     int max,c,*data;
  256.     
  257.     data = (int *)calloc(numElements,sizeof(int));
  258.     max = floor((VIEW_HEIGHT-15.0)/(ceil((float)numElements/(VIEW_WIDTH-6))));    
  259.     for (c = 0; c < numElements ;c++) {
  260.         cthread_yield();      // give the interface a change
  261.     if (((random() % 100) + 1) > percentSorted)
  262.             data[c] = (random() % (int)(max-1))+ 1;
  263.          else 
  264.         data[c] = (int)(c*(max/(float)numElements)) +1;
  265.     }
  266.     return data;
  267. }
  268.  
  269.  
  270. - setUpData;
  271. /* This method sends a message to itself to generate a new data set and 
  272.  * tUbpasses that data set along to the sorts who are scheduled to run 
  273.  * so they can make a copy of it.  The sorts will set up their sortviews 
  274.  * and draw the data set. (by messaging back to main thread)
  275.  */
  276. {    
  277.     int c, *address;
  278.        
  279.     address = [self newDataSet];
  280.     for (c = 0; c < NUM_SORT_TYPES; c++) {
  281.         if (sortOn[c]) 
  282.             [sort[c] setSize:numElements address:address];
  283.     }
  284.     cfree(address);
  285.     return self;
  286. }
  287.  
  288.  
  289. static any_t sortInThread(aSort)
  290.   id aSort;
  291. {
  292.     return [aSort sort];
  293. }
  294.  
  295.  
  296. - doSort;
  297. /* This method is called to run a complete trial.  It is launched as a thread, 
  298.  * so from here on out, I can no longer safely draw and must message to the 
  299.  * main thread.  This method generates the data for the trial, sets up all 
  300.  * the sort views, starts the tick counter, and then launches the sort threads.
  301.  */
  302. {   int c;
  303.  
  304.     [self setUpData];
  305.     if (!Abort) 
  306.         [NXApp startTickCounter]; 
  307.     for (c = 0; c < NUM_SORT_TYPES; c++) {
  308.         if (sortOn[c]) {
  309.         cthread_detach(cthread_fork(sortInThread,sort[c]));
  310.         if (!parallel) 
  311.             break;
  312.     }              
  313.     }    
  314.     return self;
  315. }
  316.  
  317.  
  318. static any_t doSort(self)
  319.   id self;
  320. {
  321.     return [self doSort];
  322. }
  323.  
  324.  
  325.  
  326. /* PUBLIC METHODS */
  327.  
  328. - (BOOL)startSort;
  329. /* The SortApp object calls this method after the Go button is clicked.  
  330.  * If there are no sorts to run or there is no data set, this method returns 
  331.  * NO to the SortApp. Otherwise, I resize and set up the sorting window and 
  332.  * launch a thread to "doSort" which generates the data and launches the sort 
  333.  * threads.  By launching a thread, this method will return almost immediately, 
  334.  * so the user isn't locked out while I do the work.
  335.  */
  336. {
  337.     if (!numSorts || !numElements) 
  338.         return NO;
  339.     else {
  340.         sortsFinished =  0;
  341.         [self setUpWindow];
  342.         cthread_detach(cthread_fork(doSort,self)); 
  343.     return YES;
  344.     } 
  345. }
  346.  
  347.  
  348. - sortFinished:(int)sortNum;
  349. /* When a sort finishes, it messages back to the main thread who does the final
  350.  * display.  The SortApp then calls this method so the SortController can 
  351.  * launch the next sort if necessary (i.e. if the sorts are running in series).  
  352.  * The SortController also keeps track of how many sorts still need to finish.  
  353.  * If all sorts have finished, the SortController lets the SortApp know, so it 
  354.  * can clean up, stop the tickUcnter and re-enable all the controls in the 
  355.  * Parameters panel.
  356.  */
  357. {
  358.     sortsFinished++;
  359.     if (sortsFinished == numSorts)     // if all sorts finished,
  360.         [NXApp allFinished];        // let SortApp know this
  361.     else if (!parallel) {        // if in series, launch next sort
  362.         while (!sortOn[++sortNum]) ;
  363.     [NXApp startTickCounter]; 
  364.         cthread_detach(cthread_fork(sortInThread,sort[sortNum]));
  365.     }
  366.   return self;
  367. }
  368.  
  369.  
  370. - findSortWithNum:(int)sortNum;
  371. /* Simply returns the sort of the specified number.  Called by the SortApp 
  372.  * who has only the number of the sort, but needs the actual sort object.
  373.  */
  374. {
  375.     return sort[sortNum];
  376. }
  377.  
  378.  
  379. @end