|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.