home *** CD-ROM | disk | FTP | other *** search
/ NeXTSTEP 3.3 (Developer) / NeXT_Developer-3.3.iso / NextDeveloper / Examples / AppKit / SortingInAction / SortController.m < prev    next >
Encoding:
Text File  |  1992-05-27  |  11.1 KB  |  380 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. #import <libc.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]];
  104.     return (!entryOK);
  105. }
  106.  
  107.  
  108.  
  109. /*  TARGET-ACTION METHODS (for controls in the Sort Parameters panel) */
  110.  
  111. - changeAnimate:sender
  112. /* Changes whether the sorts highlight their comparisions. SortController 
  113.  * simply passes this method onto each of the sorts.
  114.  */
  115. {   int c;
  116.     BOOL value = (BOOL)[sender state];
  117.     
  118.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  119.         [sort[c] setAnimate:value];
  120.     return self;
  121. }
  122.  
  123.  
  124. - changeParallel:sender
  125. /* Changes whether the sorts run in parallel or series. SortController needs 
  126.  * this to know whether to launch all sort threads at once, or to wait until 
  127.  * one has finished to start the next sort.
  128.  */
  129. {
  130.     parallel = (BOOL)[sender selectedTag];
  131.     return self;
  132. }
  133.  
  134.  
  135. - changeSpeed:sender;
  136. /* Changes at what speed the sorts are running.  SortController simply passes 
  137.  * this method onto each of the sorts.
  138.  */
  139. {
  140.     int c, speed;
  141.     speed = [(Slider *)sender maxValue] - [sender intValue];
  142.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  143.        [sort[c] setSpeed:speed];
  144.     return self;
  145. }
  146.  
  147.  
  148. - changeSortOn:sender
  149. /* Changes whether a selected sort is set to run or not.  Increments or 
  150.  * decrements the number of sorts in this trial as appropriate.
  151.  */
  152. {   BOOL on; 
  153.  
  154.     on = (BOOL)[[sender selectedCell] state];
  155.     sortOn[[sender selectedTag]] = on;
  156.     if (on) 
  157.         numSorts++;
  158.     else 
  159.         numSorts--;
  160.     return self;
  161. }
  162.  
  163.  
  164.       
  165. - changeDataSet:sender;
  166. /* Changes the size or percent sorted of a data set, uses tag to determine 
  167.  * which.
  168.  */
  169. {
  170.     switch ([sender selectedTag]) {
  171.         case SIZE:    numElements = [sender intValue];
  172.             break;
  173.         case PERCENT:   percentSorted = [sender intValue];
  174.             break;
  175.     }
  176.     return self;
  177. }
  178.  
  179.  
  180. - changeTickValue:sender;
  181. /* Changes the tick cost of a certain operation, uses tag to determine which.  
  182.  * SortController simply passes this method onto each of the sorts.
  183.  */
  184. {
  185.     int c, value;
  186.     
  187.     value = [sender intValue];
  188.     for (c = 0; c < NUM_SORT_TYPES; c++) 
  189.         [sort[c] setTicks:value for:[sender selectedTag]];
  190.     return self;
  191. }
  192.  
  193.  
  194.  
  195.  
  196. /* PRIVATE METHODS */
  197.  
  198.  
  199. - setSortWindow:anObject
  200. /* The sortWindow is where all the animation takes place.
  201.  */
  202. {   
  203.     sortWindow = anObject;  
  204.     [[sortWindow contentView] setFlipped:YES];     
  205.     return self;
  206. }
  207.  
  208.  
  209. - setUpWindow;
  210. /* This is the method that adjusts the sorting window for a new trial.  It 
  211.  * resizes the window to fit all the needed sort views and then adds those 
  212.  * sortviews as subviews of the content view.  It removes any sortviews of
  213.  * sorts that aren't scheduled to run in this trial.
  214.  */
  215. {
  216.     NXPoint pt = {2.0,2.0};
  217.     NXRect windowRect,headerRect;
  218.     float newHeight;
  219.     int c;
  220.     
  221.     [sortWindow disableFlushWindow];
  222.     [sortWindow getFrame:&windowRect];
  223.     [windowHeader getFrame:&headerRect];
  224.     newHeight = headerRect.size.height + numSorts*(VIEW_HEIGHT+2) + 24;
  225.     windowRect.origin.y += windowRect.size.height - newHeight;
  226.     windowRect.size.height = newHeight;
  227.     [sortWindow placeWindow:&windowRect];
  228.     [windowHeader moveTo:headerRect.origin.x :-3];
  229.     pt.y = headerRect.size.height;
  230.     for (c = 0; (c < NUM_SORT_TYPES); c++) {
  231.         if (sortOn[c]) {
  232.         [[sort[c] view] moveTo:pt.x :pt.y];
  233.         [[sortWindow contentView] addSubview:[sort[c] view]];
  234.         pt.y += VIEW_HEIGHT+2.0;
  235.     } else {
  236.         [[sort[c] view] removeFromSuperview]; 
  237.     }
  238.      }   
  239.      [sortWindow display];
  240.      [[[sortWindow reenableFlushWindow] flushWindow] orderFront:self];
  241.      return self;
  242. }
  243.  
  244.  
  245. - (int *)newDataSet
  246. /* This is the method to generate a new data set.  Basically, it cycles 
  247.  * through a loop numElements times, getting a new number for each position 
  248.  * of the array.  For completely random data, each element is randomly 
  249.  * generated.  For partially sorted data, only some of the elements are random, 
  250.  * others are inserted in sorted position.  Each time through the loop, a 
  251.  * random number is first generated to determine whether this array element 
  252.  * will be random. If the random number is greater than the percent sorted, 
  253.  * it will generate a random number for that array element, otherwise it will 
  254.  * assign an element in sorted order to that array element. */
  255. {    
  256.     int max,c,*data;
  257.     
  258.     data = (int *)calloc(numElements,sizeof(int));
  259.     max = floor((VIEW_HEIGHT-15.0)/(ceil((float)numElements/(VIEW_WIDTH-6))));    
  260.     for (c = 0; c < numElements ;c++) {
  261.         cthread_yield();      // give the interface a change
  262.     if (((random() % 100) + 1) > percentSorted)
  263.             data[c] = (random() % (int)(max-1))+ 1;
  264.          else 
  265.         data[c] = (int)(c*(max/(float)numElements)) +1;
  266.     }
  267.     return data;
  268. }
  269.  
  270.  
  271. - setUpData;
  272. /* This method sends a message to itself to generate a new data set and 
  273.  * then passes that data set along to the sorts who are scheduled to run 
  274.  * so they can make a copy of it.  The sorts will set up their sortviews 
  275.  * and draw the data set. (by messaging back to main thread)
  276.  */
  277. {    
  278.     int c, *address;
  279.        
  280.     address = [self newDataSet];
  281.     for (c = 0; c < NUM_SORT_TYPES; c++) {
  282.         if (sortOn[c]) 
  283.             [sort[c] setSize:numElements address:address];
  284.     }
  285.     cfree(address);
  286.     return self;
  287. }
  288.  
  289.  
  290. static any_t sortInThread(aSort)
  291.   id aSort;
  292. {
  293.     return [aSort sort];
  294. }
  295.  
  296.  
  297. - doSort;
  298. /* This method is called to run a complete trial.  It is launched as a thread, 
  299.  * so from here on out, I can no longer safely draw and must message to the 
  300.  * main thread.  This method generates the data for the trial, sets up all 
  301.  * the sort views, starts the tick counter, and then launches the sort threads.
  302.  */
  303. {   int c;
  304.  
  305.     [self setUpData];
  306.     if (!Abort) 
  307.         [NXApp startTickCounter]; 
  308.     for (c = 0; c < NUM_SORT_TYPES; c++) {
  309.         if (sortOn[c]) {
  310.         cthread_detach(cthread_fork(sortInThread,sort[c]));
  311.         if (!parallel) 
  312.             break;
  313.     }              
  314.     }    
  315.     return self;
  316. }
  317.  
  318.  
  319. static any_t doSort(self)
  320.   id self;
  321. {
  322.     return [self doSort];
  323. }
  324.  
  325.  
  326.  
  327. /* PUBLIC METHODS */
  328.  
  329. - (BOOL)startSort;
  330. /* The SortApp object calls this method after the Go button is clicked.  
  331.  * If there are no sorts to run or there is no data set, this method returns 
  332.  * NO to the SortApp. Otherwise, I resize and set up the sorting window and 
  333.  * launch a thread to "doSort" which generates the data and launches the sort 
  334.  * threads.  By launching a thread, this method will return almost immediately, 
  335.  * so the user isn't locked out while I do the work.
  336.  */
  337. {
  338.     if (!numSorts || !numElements) 
  339.         return NO;
  340.     else {
  341.         sortsFinished =  0;
  342.         [self setUpWindow];
  343.         cthread_detach(cthread_fork(doSort,self)); 
  344.     return YES;
  345.     } 
  346. }
  347.  
  348.  
  349. - sortFinished:(int)sortNum;
  350. /* When a sort finishes, it messages back to the main thread who does the final
  351.  * display.  The SortApp then calls this method so the SortController can 
  352.  * launch the next sort if necessary (i.e. if the sorts are running in series).  
  353.  * The SortController also keeps track of how many sorts still need to finish.  
  354.  * If all sorts have finished, the SortController lets the SortApp know, so it 
  355.  * can clean up, stop the tick counter and re-enable all the controls in the 
  356.  * Parameters panel.
  357.  */
  358. {
  359.     sortsFinished++;
  360.     if (sortsFinished == numSorts)     // if all sorts finished,
  361.         [NXApp allFinished];        // let SortApp know this
  362.     else if (!parallel) {        // if in series, launch next sort
  363.         while (!sortOn[++sortNum]) ;
  364.     [NXApp startTickCounter]; 
  365.         cthread_detach(cthread_fork(sortInThread,sort[sortNum]));
  366.     }
  367.   return self;
  368. }
  369.  
  370.  
  371. - findSortWithNum:(int)sortNum;
  372. /* Simply returns the sort of the specified number.  Called by the SortApp 
  373.  * who has only the number of the sort, but needs the actual sort object.
  374.  */
  375. {
  376.     return sort[sortNum];
  377. }
  378.  
  379.  
  380. @end