|
|
Sample Programs from NeXSTEP 3.3
/*
* This class is in charge of application-wide activity such as handling the
* main menu commands, enabling and disabling controls and managing the tick
* counter display. It is also responsible for receiving drawing requests
* from the sort threads. When any thread needs to draw, it conjures up a
* simple Mach message which it sends to the drawing port that the SortApp
* monitors. The SortApp will have the appropriate view do the necessary
* drawing.
*
* 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 "SortApp.h"
#import "SortController.h"
#import "GenericSort.h"
#import "SortView.h"
#import <mach/cthreads.h>
#import <string.h>
/* Global variables shared across several class/threads. */
BOOL Abort; // global variable to signal abort
int TickCount; // global variable of current tick count
mutex_t TickLock; // mutual exclusion to protect TickCount
condition_t TickUpdated; // signaled when TickCount is increased
@implementation SortApp
+ initialize;
/* +initialize is sent to the factory object for SortApp before any
* other messages are sent. This is a good place to allocate and initialize
* the global variables.
*/
{
if (self == [SortApp class]) {
Abort = NO;
TickCount = 0;
TickLock = mutex_alloc();
TickUpdated = condition_alloc();
}
return self;
}
+ new;
{
self = [super new];
teNum = (DPSTimedEntry)0;
return self;
}
/* TARGET-ACTION METHODS */
- info:sender
/*
* The info panel is a separate nib module and only loaded on demand.
*/
{
if (!infoPanel) {
if (![self loadNibSection:"InfoPanel.nib" owner:self withNames:NO]) {
NXLogError ("Could not load InfoPanel.nib");
}
}
[infoPanel makeKeyAndOrderFront:self];
return self;
}
- help:sender
/*
* The help panel is a separate nib module and only loaded on demand.
*/
{
if (!helpPanel) {
if (![self loadNibSection:"HelpPanel.nib" owner:self withNames:NO]) {
NXLogError ("Could not load HelpPanel.nib");
}
}
[helpPanel makeKeyAndOrderFront:self];
return self;
}
- go:sender
/*
* This button gets the whole thing going. It disables the controls that
* will be inactive while the sort is running, resets the tick counter, and
* asks the SortController to start the trial. The SortController will launch
* a thread to generate the data and run the sorts, so this method will return
* almost immediately. That way the user interface of the program remains
* responsive. The user can stop the sort, change the speed, go look at the
* Info panel, whatever! When a sort is running, this button becomes the Stop
* button, used to cancel a sort in progress.
*/
{
if (!strcmp([sender icon],"Stop")) // if sorting, this is "Stop" button
return [self stop:self];
if ([[goButton window] makeFirstResponder:[goButton window]]) {
if ([sortController startSort]) { // if trial got started ok
[self setControlsEnabled:NO];
[messageField setStringValue:
[stringTable valueForStringKey:"Data"]];
[tickField setIntValue:0];
}
}
return self;
}
- stop:sender
/*
* This method cancels a sort in progress the whole thing going. It stops
* the tick counter and sets the global variable Abort to YES. The sorting
* threads continually check this variable and when the recognize it they
* send one last message to the drawPort which will display the canceled
* sort. When they have all check in, the method allFinished will do any
* necessary clean up.
*/
{
Abort = YES; // threads will pick up on this & report back when killed
[messageField setStringValue:[stringTable valueForStringKey:"Canceled"]];
condition_broadcast(TickUpdated);
[self stopTickCounter];
return self;
}
- setControlsEnabled:(BOOL)value;
/* This method is called to disable all the inactive controls in the parameter
* window while a sort is in progress (the data set size, percent sorted, etc).
* This method is also called to enable those controls when the sorts have
* finished.
*/
{
[[performRadios window] disableFlushWindow];
[performRadios setEnabled:value];
[dataSetMatrix setEnabled:value];
[algorithmMatrix setEnabled:value];
[tickValueMatrix setEnabled:value];
[tickMatrix setEnabled:value];
if (value)
[goButton setIcon:"Go!"];
else
[goButton setIcon:"Stop"];
[[[performRadios window] reenableFlushWindow] flushWindow];
return self;
}
- allFinished;
/* This method is sent by the SortController when it has verified that
* all sorts have finished in the current trial. It will stop the tick
* counter, re-enable the controls, and reset Abort to NO.
*/
{
[self stopTickCounter];
[self setControlsEnabled:YES];
if (!Abort)
[messageField setStringValue:[stringTable valueForStringKey:"Done"]];
Abort = NO;
return self;
}
- setTickField:anObject
/*
* The tickField is a text field on the animation window that displays the
* tick count. When a sort is running, a timed entry is called every second
* to update the tick field. The timed entry runs at a priority one higher
* than NXMODALRESPTHRESHOLD. Because of this, the tick field is updated even
* during a modal response loop (such as changing the speed slider).
*/
{
tickField = anObject;
[tickField allocateGState]; // for more efficient lock/unlock focus
return self;
}
static void updateTick (teNum,now,self)
DPSTimedEntry teNum;
double now;
SortApp *self;
{
mutex_lock(TickLock);
[self->tickField setIntValue:TickCount]; // display current tick count
mutex_unlock(TickLock);
}
- startTickCounter;
/* This method is called to reset the tick count to zero and to kick off the
* timed entry that will update the tick field display every second.
*/
{
TickCount = 0;
if (teNum)
DPSRemoveTimedEntry(teNum);
teNum = DPSAddTimedEntry(1.0, &updateTick, self, NX_MODALRESPTHRESHOLD +1);
return self;
}
- stopTickCounter;
/* This method is called to remove the timed entry that has been updating the
* tick field every second. The tick field will display the current tick
* count.
*/
{
if (teNum)
DPSRemoveTimedEntry(teNum);
teNum = (DPSTimedEntry)0;
[tickField setIntValue:TickCount];
return self;
}
- stringTable;
/* This method simply returns the string table for other objects which need to
* access strings.
*/
{
return stringTable;
}
- draw:(simpleMsg *)msg;
/* This is the method called by the message handler to do draw requests from
* the sorting threads. The messages are configured so that the msg_id field
* of the message header indicates what type of message it is (swap, move,
* compare, or finished). The first integer in the message is the sortNum
* which is the unique integer for the sort sending the message. This integer
* can be passed to the SortController to get the Sort object for that sort,
* which can be used to get its SortView. The other integers passed in the
* message are the parameters for the different draw messages (the values to
* swap, the positions to compare, etc). The SortApp then messages the
* appropriate SortView, passing the parameters, and the SortView will do the
* drawing.
*/
{
id view;
view = [[sortController findSortWithNum:msg->sortNum] view];
switch(msg->h.msg_id) {
case SORTING_MSG: if (!Abort) [messageField setStringValue:
[stringTable valueForStringKey:"Sorting"]];
break;
case COMPARE_MSG: [view compare:msg->position[0] value:msg->value[0]
with:msg->position[1] value:msg->value[1]];
break;
case SWAP_MSG: [view swap:msg->position[0] value:msg->value[0]
with:msg->position[1] value:msg->value[1]];
break;
case MOVE_MSG: [view moveValue:msg->value[0] to:msg->position[0]
oldValue:msg->value[1]];
break;
case FINISHED_MSG: [view displayFinished];
[sortController sortFinished:msg->sortNum];
break;
}
return self;
}
static void msgHandler(msg,self)
simpleMsg *msg;
id self;
{
[self draw:msg];
}
- (port_t)drawPort;
/* This method returns the port allocated by the main thread. All drawing
* messages are sent to this port.
*/
{
return drawPort;
}
/* DELEGATE METHODS */
- appDidInit:sender
/* This method allocates the port for the main thread where all drawing
* messages are sent. Then, it adds the port to those monitored by the
* application. Whenever a message is sent to this port, the handler
* function is called to draw the message. This port is registered at a
* priority of one greater than NX_MODAL RESPTHRESHOLD. Because of this,
* the drawing messages will continue to be processed, even during a modal
* response loop (such as changing the speed slider). The speed slider sends
* message continuously so you can dynamically change the speed and immediately
* see the effects of those changes.
*/
{
port_allocate(task_self(), &drawPort);
DPSAddPort(drawPort,msgHandler,sizeof(simpleMsg),self,NX_MODALRESPTHRESHOLD +1);
return self;
}
@end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.