Annotation of Examples/AppKit/SortingInAction/SortApp.m, revision 1.1.1.1

1.1       root        1: 
                      2: /*
                      3:  * This class is in charge of application-wide activity such as handling the
                      4:  * main menu commands, enabling and disabling controls and managing the tick
                      5:  * counter display.  It is also responsible for receiving drawing requests 
                      6:  * from the sort threads.  When any thread needs to draw, it conjures up a 
                      7:  * simple Mach message which it sends to the drawing port that the SortApp 
                      8:  * monitors.  The SortApp will have the appropriate view do the necessary
                      9:  * drawing.
                     10:  *
                     11:  * Author: Julie Zelenski, NeXT Developer Support
                     12:  * You may freely copy, distribute and reuse the code in this example.  
                     13:  * NeXT disclaims any warranty of any kind, expressed or implied, as to 
                     14:  * its fitness for any particular use.
                     15:  */
                     16:  
                     17: #import <appkit/appkit.h>
                     18: #import "SortApp.h"
                     19: #import "SortController.h"
                     20: #import "GenericSort.h"
                     21: #import "SortView.h"
                     22: #import <mach/cthreads.h>
                     23: #import <string.h>
                     24: 
                     25: 
                     26: /* Global variables shared across several class/threads. */
                     27: 
                     28: BOOL Abort;                    // global variable to signal abort 
                     29: int TickCount;                 // global variable of current tick count 
                     30: mutex_t TickLock;              // mutual exclusion to protect TickCount 
                     31: condition_t TickUpdated;       // signaled when TickCount is increased 
                     32: 
                     33: 
                     34: @implementation SortApp
                     35: 
                     36: 
                     37: + initialize;
                     38: /* +initialize is sent to the factory object for SortApp before any 
                     39:  * other messages are sent.  This is a good place to allocate and initialize 
                     40:  * the global variables.
                     41:  */
                     42: {
                     43:     if (self == [SortApp class]) {
                     44:        Abort = NO;
                     45:        TickCount = 0;
                     46:        TickLock = mutex_alloc();
                     47:        TickUpdated = condition_alloc();
                     48:     }
                     49:     return self;
                     50: }
                     51: 
                     52: 
                     53: + new;
                     54: {  
                     55:     self = [super new];
                     56:     teNum = (DPSTimedEntry)0;
                     57:     return self;
                     58: }
                     59: 
                     60: 
                     61: 
                     62: /* TARGET-ACTION METHODS */
                     63: 
                     64: - info:sender
                     65: /*
                     66:  * The info panel is a separate nib module and only loaded on demand.
                     67:  */
                     68: {
                     69:     if (!infoPanel) {
                     70:        if (![self loadNibSection:"InfoPanel.nib" owner:self withNames:NO]) {
                     71:            NXLogError ("Could not load InfoPanel.nib");
                     72:        }
                     73:     }
                     74:     [infoPanel makeKeyAndOrderFront:self];
                     75:     return self;
                     76: }
                     77: 
                     78: 
                     79: - help:sender
                     80: /*
                     81:  * The help panel is a separate nib module and only loaded on demand.
                     82:  */
                     83: {
                     84:     if (!helpPanel) {
                     85:        if (![self loadNibSection:"HelpPanel.nib" owner:self withNames:NO]) {
                     86:            NXLogError ("Could not load HelpPanel.nib");
                     87:        }
                     88:     }
                     89:     [helpPanel makeKeyAndOrderFront:self];
                     90:     return self;
                     91: }
                     92: 
                     93: 
                     94: - go:sender
                     95: /*
                     96:  * This button gets the whole thing going.  It disables the controls that 
                     97:  * will be inactive while the sort is running, resets the tick counter, and 
                     98:  * asks the SortController to start the trial.  The SortController will launch 
                     99:  * a thread to generate the data and run the sorts, so this method will return 
                    100:  * almost immediately.  That way the user interface of the program remains 
                    101:  * responsive.  The user can stop the sort, change the speed, go look at the 
                    102:  * Info panel, whatever!  When a sort is running, this button becomes the Stop 
                    103:  * button, used to cancel a sort in progress.
                    104:  */
                    105: {
                    106:     if (!strcmp([sender icon],"Stop")) // if sorting, this is "Stop" button
                    107:         return [self stop:self];
                    108:     if ([[goButton window] makeFirstResponder:[goButton window]]) {
                    109:        if ([sortController startSort]) { // if trial got started ok
                    110:            [self setControlsEnabled:NO];
                    111:            [messageField setStringValue:
                    112:                        [stringTable valueForStringKey:"Data"]];
                    113:            [tickField setIntValue:0];
                    114:        }
                    115:     } 
                    116:     return self;
                    117: }
                    118: 
                    119: 
                    120: - stop:sender
                    121: /*
                    122:  * This method cancels a sort in progress the whole thing going.  It stops 
                    123:  * the tick counter and sets the global variable Abort to YES.  The sorting 
                    124:  * threads continually check this variable and when the recognize it they 
                    125:  * send one last message to the drawPort which will display the canceled 
                    126:  * sort. When they have all check in, the method allFinished will do any 
                    127:  * necessary clean up. 
                    128:  */
                    129: {
                    130:     Abort = YES;    // threads will pick up on this & report back when killed
                    131:     [messageField setStringValue:[stringTable valueForStringKey:"Canceled"]];
                    132:     condition_broadcast(TickUpdated); 
                    133:     [self stopTickCounter];
                    134:     return self;
                    135: }
                    136: 
                    137: 
                    138: - setControlsEnabled:(BOOL)value;
                    139: /* This method is called to disable all the inactive controls in the parameter 
                    140:  * window while a sort is in progress (the data set size, percent sorted, etc).  
                    141:  * This method is also called to enable those controls when the sorts have
                    142:  * finished.
                    143:  */
                    144: {
                    145:     [[performRadios window] disableFlushWindow];
                    146:     [performRadios setEnabled:value];
                    147:     [dataSetMatrix setEnabled:value];
                    148:     [algorithmMatrix setEnabled:value];
                    149:     [tickValueMatrix setEnabled:value];
                    150:     [tickMatrix setEnabled:value];
                    151:     if (value) 
                    152:        [goButton setIcon:"Go!"];
                    153:     else 
                    154:        [goButton setIcon:"Stop"];
                    155:     [[[performRadios window] reenableFlushWindow] flushWindow];
                    156:     return self;
                    157: }
                    158: 
                    159: 
                    160: - allFinished;
                    161: /* This method is sent by the SortController when it has verified that 
                    162:  * all sorts have finished in the current trial.  It will stop the tick 
                    163:  * counter, re-enable the controls, and reset Abort to NO.
                    164:  */
                    165: {
                    166:     [self stopTickCounter];
                    167:     [self setControlsEnabled:YES];
                    168:     if (!Abort) 
                    169:         [messageField setStringValue:[stringTable valueForStringKey:"Done"]];
                    170:     Abort = NO;
                    171:     return self;
                    172: }
                    173: 
                    174: 
                    175: - setTickField:anObject
                    176: /*
                    177:  * The tickField is a text field on the animation window that displays the
                    178:  * tick count.  When a sort is running, a timed entry is called every second 
                    179:  * to update the tick field.  The timed entry runs at a priority one higher
                    180:  * than NXMODALRESPTHRESHOLD.  Because of this, the tick field is updated even 
                    181:  * during a modal response loop (such as changing the speed slider).
                    182:  */
                    183: { 
                    184:     tickField = anObject;     
                    185:     [tickField allocateGState];        // for more efficient lock/unlock focus
                    186:     return self; 
                    187: }
                    188: 
                    189: 
                    190: static void updateTick (teNum,now,self)
                    191:   DPSTimedEntry teNum;
                    192:   double now;
                    193:   SortApp *self;
                    194: {
                    195:     mutex_lock(TickLock);
                    196:     [self->tickField setIntValue:TickCount];   // display current tick count
                    197:     mutex_unlock(TickLock);
                    198: }
                    199:  
                    200:     
                    201: - startTickCounter;
                    202: /* This method is called to reset the tick count to zero and to kick off the 
                    203:  * timed entry that will update the tick field display every second.
                    204:  */
                    205: {
                    206:     TickCount = 0;
                    207:     if (teNum) 
                    208:         DPSRemoveTimedEntry(teNum);
                    209:     teNum = DPSAddTimedEntry(1.0, &updateTick, self, NX_MODALRESPTHRESHOLD +1);
                    210:     return self;
                    211: }
                    212: 
                    213: 
                    214: - stopTickCounter;
                    215: /* This method is called to remove the timed entry that has been updating the 
                    216:  * tick field every second.  The tick field will display the current tick 
                    217:  * count.
                    218:  */
                    219: {
                    220:     if (teNum) 
                    221:         DPSRemoveTimedEntry(teNum);
                    222:     teNum = (DPSTimedEntry)0;
                    223:     [tickField setIntValue:TickCount];
                    224:     return self;
                    225: }
                    226: 
                    227: 
                    228: - stringTable;
                    229: /* This method simply returns the string table for other objects which need to 
                    230:  * access strings.
                    231:  */
                    232: {
                    233:     return stringTable;
                    234: }
                    235: 
                    236: - draw:(simpleMsg *)msg;
                    237: /* This is the method called by the message handler to do draw requests from 
                    238:  * the sorting threads.  The messages are configured so that the msg_id field 
                    239:  * of the message header indicates what type of message it is (swap, move, 
                    240:  * compare, or finished).  The first integer in the message is the sortNum 
                    241:  * which is the unique integer for the sort sending the message. This integer 
                    242:  * can be passed to the SortController to get the Sort object for that sort, 
                    243:  * which can be used to get its SortView.   The other integers passed in the 
                    244:  * message are the parameters for the different draw messages (the values to 
                    245:  * swap, the positions to compare, etc).  The SortApp then messages the 
                    246:  * appropriate SortView, passing the parameters, and the SortView will do the 
                    247:  * drawing.
                    248:  */
                    249: {
                    250:     id view;
                    251:     
                    252:     view = [[sortController findSortWithNum:msg->sortNum] view];
                    253:     switch(msg->h.msg_id) {
                    254:         case SORTING_MSG:   if (!Abort) [messageField setStringValue:
                    255:                                [stringTable valueForStringKey:"Sorting"]];
                    256:                            break;
                    257:        case COMPARE_MSG:   [view compare:msg->position[0] value:msg->value[0]
                    258:                            with:msg->position[1] value:msg->value[1]];
                    259:                            break;
                    260:         case SWAP_MSG:      [view swap:msg->position[0] value:msg->value[0]
                    261:                            with:msg->position[1] value:msg->value[1]];
                    262:                            break;
                    263:         case MOVE_MSG:      [view moveValue:msg->value[0] to:msg->position[0] 
                    264:                            oldValue:msg->value[1]];
                    265:                            break;
                    266:         case FINISHED_MSG:  [view displayFinished];
                    267:                            [sortController sortFinished:msg->sortNum];
                    268:                            break;
                    269:     }
                    270:     return self;
                    271: }
                    272: 
                    273: static void msgHandler(msg,self)
                    274:   simpleMsg *msg;
                    275:   id self;
                    276: {    
                    277:     [self draw:msg];
                    278: }
                    279: 
                    280: 
                    281: - (port_t)drawPort;
                    282: /* This method returns the port allocated by the main thread.  All drawing 
                    283:  * messages are sent to this port. 
                    284:  */
                    285: {
                    286:    return drawPort;
                    287: }
                    288: 
                    289: 
                    290: /* DELEGATE METHODS */
                    291: 
                    292: - appDidInit:sender
                    293: /* This method allocates the port for the main thread where all drawing 
                    294:  * messages are sent.  Then, it adds the port to those monitored by the 
                    295:  * application.  Whenever a message is sent to this port, the handler 
                    296:  * function is called to draw the message.  This port is registered at a 
                    297:  * priority of one greater than NX_MODAL RESPTHRESHOLD.  Because of this, 
                    298:  * the drawing messages will continue to be processed, even during a modal 
                    299:  * response loop (such as changing the speed slider).  The speed slider sends 
                    300:  * message continuously so you can dynamically change the speed and immediately 
                    301:  * see the effects of those changes.
                    302:  */
                    303: {      
                    304:     port_allocate(task_self(), &drawPort);
                    305:     DPSAddPort(drawPort,msgHandler,sizeof(simpleMsg),self,NX_MODALRESPTHRESHOLD +1);
                    306:     return self;
                    307: }
                    308: 
                    309: @end

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.