|
|
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.