This is SortController.m in view mode; [Download] [Up]
/* The SortController class is the "brains" of the sorting operations. As the * shepherd of a flock of sorts, it keeps an array of Sort objects. The * SortController starts off the sort trials when the SortApp gets the "go!" * signal. The SortController generates the data and forks off the sort * threads. It keeps track of when the sorts finish, starts the next sort * when the sorts are running in series, and lets the SortApp know when all * the sorts have finished. Most of the controls of the Parameters panel send * their target-action methods to the SortController and it sends the changes * on to the sorts as necessary. The SortController is also the text delegate * for the various text fields and forms of the Parameters panel. It does * entry checking to make sure the user enters reasonable values. * * Author: Julie Zelenski, NeXT Developer Support * You may freely copy, distribute and reuse the code in this example. * NeXT disclaims any warranty of any kind, expressed or implied, as to * its fitness for any particular use. */ #import <appkit/appkit.h> #import "SortController.h" #import "BubbleSort.h" #import "InsertionSort.h" #import "MergeSort.h" #import "QuickSort.h" #import "SelectionSort.h" #import "ShellSort.h" #import "SortApp.h" #import "SortView.h" #import <libc.h> #import <math.h> #import <threadkit/threadkit.h> extern BOOL Abort; // global variable to signal abort @implementation SortController:Object - init; /* The initialization method is called once, when unarchiving the nib section. * There is only one instantiation of the SortConroller object. This method * creates a new sort object for each algorithm and initializes some instance * variables. */ { [super init]; srandom(time(0)); // Seed random for data generating // Create on of each sort object sort[BUBBLE_SORT] = [[BubbleSort allocFromZone:[self zone]] init]; sort[INSERTION_SORT] = [[InsertionSort allocFromZone:[self zone]] init]; sort[MERGE_SORT] = [[MergeSort allocFromZone:[self zone]] init]; sort[QUICK_SORT] = [[QuickSort allocFromZone:[self zone]] init]; sort[SELECTION_SORT] = [[SelectionSort allocFromZone:[self zone]] init]; sort[SHELL_SORT] = [[ShellSort allocFromZone:[self zone]] init]; sortOn[QUICK_SORT] = sortOn[INSERTION_SORT] = sortOn[SHELL_SORT] = YES; sortOn[BUBBLE_SORT] = sortOn[SELECTION_SORT] = sortOn[MERGE_SORT] = NO; numSorts = 3; parallel = YES; numElements = 50; percentSorted = 0; return self; } #define SIZE 1 // tags for different text fields #define PERCENT 2 #define COMPARE 3 #define MOVE 4 #define FCALL 5 /* TEXT DELEGATE METHODS */ - (BOOL)textWillEnd:textObject; /* Checks to make sure that the entry in each of the text fields is reasonable. * Different values are reasonable based on what the field is. You can get the * field or form being edited by asking the text object for its delegate. If * the value is unreasonable, reject it, and set the value back to the closest * reasonable value. Then it sends the action to the target so that change is * recorded. */ { BOOL entryOK = YES; id form; int value; form = [textObject delegate]; value = [[form selectedCell] intValue]; switch ([form selectedTag]) { case SIZE: entryOK = (value > 0 ); if (value>5000) [[form selectedCell] setIntValue:5000]; break; case PERCENT: entryOK = ((value >= 0) && (value <= 100)); break; case FCALL: case MOVE: case COMPARE: entryOK = (value >= 0); break; } if (entryOK) [form sendAction:[form action] to:[form target]]; return (!entryOK); } /* TARGET-ACTION METHODS (for controls in the Sort Parameters panel) */ - changeAnimate:sender /* Changes whether the sorts highlight their comparisions. SortController * simply passes this method onto each of the sorts. */ { int c; BOOL value = (BOOL)[sender state]; for (c = 0; c < NUM_SORT_TYPES; c++) [sort[c] setAnimate:value]; return self; } - changeParallel:sender /* Changes whether the sorts run in parallel or series. SortController needs * this to know whether to launch all sort threads at once, or to wait until * one has finished to start the next sort. */ { parallel = (BOOL)[sender selectedTag]; return self; } - changeSpeed:sender; /* Changes at what speed the sorts are running. SortController simply passes * this method onto each of the sorts. */ { int c, speed; speed = [(Slider *)sender maxValue] - [sender intValue]; for (c = 0; c < NUM_SORT_TYPES; c++) [sort[c] setSpeed:speed]; return self; } - changeSortOn:sender /* Changes whether a selected sort is set to run or not. Increments or * decrements the number of sorts in this trial as appropriate. */ { BOOL on; on = (BOOL)[[sender selectedCell] state]; sortOn[[sender selectedTag]] = on; if (on) numSorts++; else numSorts--; return self; } - changeDataSet:sender; /* Changes the size or percent sorted of a data set, uses tag to determine * which. */ { switch ([sender selectedTag]) { case SIZE: numElements = [sender intValue]; break; case PERCENT: percentSorted = [sender intValue]; break; } return self; } - changeTickValue:sender; /* Changes the tick cost of a certain operation, uses tag to determine which. * SortController simply passes this method onto each of the sorts. */ { int c, value; value = [sender intValue]; for (c = 0; c < NUM_SORT_TYPES; c++) [sort[c] setTicks:value for:[sender selectedTag]]; return self; } /* PRIVATE METHODS */ - setSortWindow:anObject /* The sortWindow is where all the animation takes place. */ { sortWindow = anObject; [[sortWindow contentView] setFlipped:YES]; return self; } - setUpWindow; /* This is the method that adjusts the sorting window for a new trial. It * resizes the window to fit all the needed sort views and then adds those * sortviews as subviews of the content view. It removes any sortviews of * sorts that aren't scheduled to run in this trial. */ { NXPoint pt = {2.0,2.0}; NXRect windowRect,headerRect; float newHeight; int c; [sortWindow disableFlushWindow]; [sortWindow getFrame:&windowRect]; [windowHeader getFrame:&headerRect]; newHeight = headerRect.size.height + numSorts*(VIEW_HEIGHT+2) + 24; windowRect.origin.y += windowRect.size.height - newHeight; windowRect.size.height = newHeight; [sortWindow placeWindow:&windowRect]; [windowHeader moveTo:headerRect.origin.x :-3]; pt.y = headerRect.size.height; for (c = 0; (c < NUM_SORT_TYPES); c++) { if (sortOn[c]) { [[sort[c] view] moveTo:pt.x :pt.y]; [[sortWindow contentView] addSubview:[sort[c] view]]; pt.y += VIEW_HEIGHT+2.0; } else { [[sort[c] view] removeFromSuperview]; } } [sortWindow display]; [[[sortWindow reenableFlushWindow] flushWindow] orderFront:self]; return self; } - (int *)newDataSet /* This is the method to generate a new data set. Basically, it cycles * through a loop numElements times, getting a new number for each position * of the array. For completely random data, each element is randomly * generated. For partially sorted data, only some of the elements are random, * others are inserted in sorted position. Each time through the loop, a * random number is first generated to determine whether this array element * will be random. If the random number is greater than the percent sorted, * it will generate a random number for that array element, otherwise it will * assign an element in sorted order to that array element. */ { int max,c,*data; data = (int *)calloc(numElements,sizeof(int)); max = floor((VIEW_HEIGHT-15.0)/(ceil((float)numElements/(VIEW_WIDTH-6)))); for (c = 0; c < numElements ;c++) { [ TKThread yield ] ; // Give the interface a change if (((random() % 100) + 1) > percentSorted) data[c] = (random() % (int)(max-1))+ 1; else data[c] = (int)(c*(max/(float)numElements)) +1; } return data; } - setUpData; /* This method sends a message to itself to generate a new data set and * then passes that data set along to the sorts who are scheduled to run * so they can make a copy of it. The sorts will set up their sortviews * and draw the data set. (by messaging back to main thread) */ { int c, *address; address = [self newDataSet]; for (c = 0; c < NUM_SORT_TYPES; c++) { if (sortOn[c]) [sort[c] setSize:numElements address:address]; } cfree(address); return self; } - doSort; /* This method is called to run a complete trial. It is launched as a thread, * so from here on out, I can no longer safely draw and must message to the * main thread. This method generates the data for the trial, sets up all * the sort views, starts the tick counter, and then launches the sort threads. */ { int c; [self setUpData]; if (!Abort) [NXApp startTickCounter]; for (c = 0; c < NUM_SORT_TYPES; c++) { if (sortOn[c]) { [ [ TKThread alloc ] initFor: sort[c] perform: @selector ( sort ) ] ; if (!parallel) break; } } return self; } /* PUBLIC METHODS */ - (BOOL)startSort; /* The SortApp object calls this method after the Go button is clicked. * If there are no sorts to run or there is no data set, this method returns * NO to the SortApp. Otherwise, I resize and set up the sorting window and * launch a thread to "doSort" which generates the data and launches the sort * threads. By launching a thread, this method will return almost immediately, * so the user isn't locked out while I do the work. */ { if (!numSorts || !numElements) return NO; else { sortsFinished = 0; [self setUpWindow]; [ [ TKThread alloc ] initFor: self perform: @selector ( doSort ) ] ; return YES; } } - sortFinished:(int)sortNum; /* When a sort finishes, it messages back to the main thread who does the final * display. The SortApp then calls this method so the SortController can * launch the next sort if necessary (i.e. if the sorts are running in series). * The SortController also keeps track of how many sorts still need to finish. * If all sorts have finished, the SortController lets the SortApp know, so it * can clean up, stop the tick counter and re-enable all the controls in the * Parameters panel. */ { sortsFinished++; if (sortsFinished == numSorts) // if all sorts finished, [NXApp allFinished]; // let SortApp know this else if (!parallel) { // if in series, launch next sort while (!sortOn[++sortNum]) ; [NXApp startTickCounter]; [ [ TKThread alloc ] initFor: sort[sortNum] perform: @selector ( sort ) ] ; } return self; } - findSortWithNum:(int)sortNum; /* Simply returns the sort of the specified number. Called by the SortApp * who has only the number of the sort, but needs the actual sort object. */ { return sort[sortNum]; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.