home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * 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