Annotation of Examples/AppKit/SortingInAction/SortApp.m, revision 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.