|
|
Sample Programs from NeXSTEP 3.3
/* 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 <mach/cthreads.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++) {
cthread_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;
}
static any_t sortInThread(aSort)
id aSort;
{
return [aSort sort];
}
- 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]) {
cthread_detach(cthread_fork(sortInThread,sort[c]));
if (!parallel)
break;
}
}
return self;
}
static any_t doSort(self)
id self;
{
return [self doSort];
}
/* 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];
cthread_detach(cthread_fork(doSort,self));
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];
cthread_detach(cthread_fork(sortInThread,sort[sortNum]));
}
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.