]>
Commit | Line | Data |
---|---|---|
1 | /* w_graph.c | |
2 | * | |
3 | * Micropolis, Unix Version. This game was released for the Unix platform | |
4 | * in or about 1990 and has been modified for inclusion in the One Laptop | |
5 | * Per Child program. Copyright (C) 1989 - 2007 Electronic Arts Inc. If | |
6 | * you need assistance with this program, you may contact: | |
7 | * http://wiki.laptop.org/go/Micropolis or email micropolis@laptop.org. | |
8 | * | |
9 | * This program is free software: you can redistribute it and/or modify | |
10 | * it under the terms of the GNU General Public License as published by | |
11 | * the Free Software Foundation, either version 3 of the License, or (at | |
12 | * your option) any later version. | |
13 | * | |
14 | * This program is distributed in the hope that it will be useful, but | |
15 | * WITHOUT ANY WARRANTY; without even the implied warranty of | |
16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
17 | * General Public License for more details. You should have received a | |
18 | * copy of the GNU General Public License along with this program. If | |
19 | * not, see <http://www.gnu.org/licenses/>. | |
20 | * | |
21 | * ADDITIONAL TERMS per GNU GPL Section 7 | |
22 | * | |
23 | * No trademark or publicity rights are granted. This license does NOT | |
24 | * give you any right, title or interest in the trademark SimCity or any | |
25 | * other Electronic Arts trademark. You may not distribute any | |
26 | * modification of this program using the trademark SimCity or claim any | |
27 | * affliation or association with Electronic Arts Inc. or its employees. | |
28 | * | |
29 | * Any propagation or conveyance of this program must include this | |
30 | * copyright notice and these terms. | |
31 | * | |
32 | * If you convey this program (or any modifications of it) and assume | |
33 | * contractual liability for the program to recipients of it, you agree | |
34 | * to indemnify Electronic Arts for any liability that those contractual | |
35 | * assumptions impose on Electronic Arts. | |
36 | * | |
37 | * You may not misrepresent the origins of this program; modified | |
38 | * versions of the program must be marked as such and not identified as | |
39 | * the original program. | |
40 | * | |
41 | * This disclaimer supplements the one included in the General Public | |
42 | * License. TO THE FULLEST EXTENT PERMISSIBLE UNDER APPLICABLE LAW, THIS | |
43 | * PROGRAM IS PROVIDED TO YOU "AS IS," WITH ALL FAULTS, WITHOUT WARRANTY | |
44 | * OF ANY KIND, AND YOUR USE IS AT YOUR SOLE RISK. THE ENTIRE RISK OF | |
45 | * SATISFACTORY QUALITY AND PERFORMANCE RESIDES WITH YOU. ELECTRONIC ARTS | |
46 | * DISCLAIMS ANY AND ALL EXPRESS, IMPLIED OR STATUTORY WARRANTIES, | |
47 | * INCLUDING IMPLIED WARRANTIES OF MERCHANTABILITY, SATISFACTORY QUALITY, | |
48 | * FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT OF THIRD PARTY | |
49 | * RIGHTS, AND WARRANTIES (IF ANY) ARISING FROM A COURSE OF DEALING, | |
50 | * USAGE, OR TRADE PRACTICE. ELECTRONIC ARTS DOES NOT WARRANT AGAINST | |
51 | * INTERFERENCE WITH YOUR ENJOYMENT OF THE PROGRAM; THAT THE PROGRAM WILL | |
52 | * MEET YOUR REQUIREMENTS; THAT OPERATION OF THE PROGRAM WILL BE | |
53 | * UNINTERRUPTED OR ERROR-FREE, OR THAT THE PROGRAM WILL BE COMPATIBLE | |
54 | * WITH THIRD PARTY SOFTWARE OR THAT ANY ERRORS IN THE PROGRAM WILL BE | |
55 | * CORRECTED. NO ORAL OR WRITTEN ADVICE PROVIDED BY ELECTRONIC ARTS OR | |
56 | * ANY AUTHORIZED REPRESENTATIVE SHALL CREATE A WARRANTY. SOME | |
57 | * JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF OR LIMITATIONS ON IMPLIED | |
58 | * WARRANTIES OR THE LIMITATIONS ON THE APPLICABLE STATUTORY RIGHTS OF A | |
59 | * CONSUMER, SO SOME OR ALL OF THE ABOVE EXCLUSIONS AND LIMITATIONS MAY | |
60 | * NOT APPLY TO YOU. | |
61 | */ | |
62 | #include "sim.h" | |
63 | ||
64 | ||
65 | short NewGraph = 0; | |
66 | short AllMax; | |
67 | unsigned char *History10[HISTORIES]; | |
68 | unsigned char *History120[HISTORIES]; | |
69 | int HistoryInitialized = 0; | |
70 | short Graph10Max, Graph120Max; | |
71 | Tcl_HashTable GraphCmds; | |
72 | int GraphUpdateTime = 100; | |
73 | ||
74 | void DoUpdateGraph(SimGraph *graph); | |
75 | void DoNewGraph(SimGraph *graph); | |
76 | void DoResizeGraph(SimGraph *graph, int w, int h); | |
77 | void InitNewGraph(SimGraph *graph); | |
78 | ||
79 | ||
80 | #define DEF_GRAPH_FONT "-Adobe-Helvetica-Bold-R-Normal-*-140-*" | |
81 | #define DEF_GRAPH_BG_COLOR "#b0b0b0" | |
82 | #define DEF_GRAPH_BG_MONO "#ffffff" | |
83 | #define DEF_GRAPH_BORDER_WIDTH "0" | |
84 | #define DEF_GRAPH_RELIEF "flat" | |
85 | ||
86 | Tk_ConfigSpec GraphConfigSpecs[] = { | |
87 | {TK_CONFIG_FONT, "-font", (char *) NULL, (char *) NULL, | |
88 | DEF_GRAPH_FONT, Tk_Offset(SimGraph, fontPtr), 0}, | |
89 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
90 | DEF_GRAPH_BG_COLOR, Tk_Offset(SimGraph, border), | |
91 | TK_CONFIG_COLOR_ONLY}, | |
92 | {TK_CONFIG_BORDER, "-background", "background", "Background", | |
93 | DEF_GRAPH_BG_MONO, Tk_Offset(SimGraph, border), | |
94 | TK_CONFIG_MONO_ONLY}, | |
95 | {TK_CONFIG_PIXELS, "-borderwidth", "borderWidth", "BorderWidth", | |
96 | DEF_GRAPH_BORDER_WIDTH, Tk_Offset(SimGraph, borderWidth), 0}, | |
97 | {TK_CONFIG_RELIEF, "-relief", "relief", "Relief", | |
98 | DEF_GRAPH_RELIEF, Tk_Offset(SimGraph, relief), 0}, | |
99 | {TK_CONFIG_END, (char *) NULL, (char *) NULL, (char *) NULL, | |
100 | (char *) NULL, 0, 0} | |
101 | }; | |
102 | ||
103 | ||
104 | XDisplay *FindXDisplay(); | |
105 | ||
106 | ||
107 | static void | |
108 | DisplaySimGraph(ClientData clientData) | |
109 | { | |
110 | SimGraph *graph = (SimGraph *) clientData; | |
111 | Tk_Window tkwin = graph->tkwin; | |
112 | ||
113 | graph->flags &= ~VIEW_REDRAW_PENDING; | |
114 | ||
115 | //fprintf(stderr, "DisplaySimGraph token %d\n", graph->draw_graph_token); | |
116 | ||
117 | assert(graph->draw_graph_token != 0); | |
118 | ||
119 | if (graph->draw_graph_token != 0) { | |
120 | // Tk_DeleteTimerHandler(graph->draw_graph_token); | |
121 | graph->draw_graph_token = 0; | |
122 | } | |
123 | ||
124 | if (graph->visible && (tkwin != NULL) && Tk_IsMapped(tkwin)) { | |
125 | DoUpdateGraph(graph); | |
126 | } | |
127 | } | |
128 | ||
129 | ||
130 | void | |
131 | DestroySimGraph(ClientData clientData) | |
132 | { | |
133 | SimGraph *graph = (SimGraph *) clientData; | |
134 | ||
135 | DestroyGraph(graph); | |
136 | } | |
137 | ||
138 | ||
139 | void | |
140 | EventuallyRedrawGraph(SimGraph *graph) | |
141 | { | |
142 | if (!(graph->flags & VIEW_REDRAW_PENDING)) { | |
143 | assert(graph->draw_graph_token == 0); | |
144 | if (graph->draw_graph_token == 0) { | |
145 | graph->draw_graph_token = | |
146 | Tk_CreateTimerHandler( | |
147 | GraphUpdateTime, | |
148 | DisplaySimGraph, | |
149 | (ClientData) graph); | |
150 | graph->flags |= VIEW_REDRAW_PENDING; | |
151 | //fprintf(stderr, "EventuallyRedrawGraph token %d\n", graph->draw_graph_token); | |
152 | } | |
153 | } | |
154 | } | |
155 | ||
156 | ||
157 | void | |
158 | SimGraphEventProc(ClientData clientData, XEvent *eventPtr) | |
159 | { | |
160 | SimGraph *graph = (SimGraph *) clientData; | |
161 | ||
162 | if ((eventPtr->type == Expose) && (eventPtr->xexpose.count == 0)) { | |
163 | graph->visible = 1; | |
164 | EventuallyRedrawGraph(graph); | |
165 | } else if (eventPtr->type == MapNotify) { | |
166 | graph->visible = 1; | |
167 | } else if (eventPtr->type == UnmapNotify) { | |
168 | graph->visible = 0; | |
169 | } else if (eventPtr->type == VisibilityNotify) { | |
170 | if (eventPtr->xvisibility.state == VisibilityFullyObscured) | |
171 | graph->visible = 0; | |
172 | else | |
173 | graph->visible = 1; | |
174 | } else if (eventPtr->type == ConfigureNotify) { | |
175 | DoResizeGraph(graph, | |
176 | eventPtr->xconfigure.width, | |
177 | eventPtr->xconfigure.height); | |
178 | EventuallyRedrawGraph(graph); | |
179 | } else if (eventPtr->type == DestroyNotify) { | |
180 | Tcl_DeleteCommand(graph->interp, Tk_PathName(graph->tkwin)); | |
181 | graph->tkwin = NULL; | |
182 | if (graph->flags & VIEW_REDRAW_PENDING) { | |
183 | //fprintf(stderr, "SimGraphEventProc Destroy token %d\n", graph->draw_graph_token); | |
184 | assert(graph->draw_graph_token != 0); | |
185 | if (graph->draw_graph_token != 0) { | |
186 | Tk_DeleteTimerHandler(graph->draw_graph_token); | |
187 | graph->draw_graph_token = 0; | |
188 | } | |
189 | graph->flags &= ~VIEW_REDRAW_PENDING; | |
190 | } | |
191 | Tk_EventuallyFree((ClientData) graph, DestroySimGraph); | |
192 | } | |
193 | } | |
194 | ||
195 | ||
196 | int GraphCmdconfigure(GRAPH_ARGS) | |
197 | { | |
198 | int result = TCL_OK; | |
199 | ||
200 | if (argc == 2) { | |
201 | result = Tk_ConfigureInfo(interp, graph->tkwin, GraphConfigSpecs, | |
202 | (char *) graph, (char *) NULL, 0); | |
203 | } else if (argc == 3) { | |
204 | result = Tk_ConfigureInfo(interp, graph->tkwin, GraphConfigSpecs, | |
205 | (char *) graph, argv[2], 0); | |
206 | } else { | |
207 | result = ConfigureSimGraph(interp, graph, argc-2, argv+2, | |
208 | TK_CONFIG_ARGV_ONLY); | |
209 | } | |
210 | return TCL_OK; | |
211 | } | |
212 | ||
213 | ||
214 | int GraphCmdposition(GRAPH_ARGS) | |
215 | { | |
216 | if ((argc != 2) && (argc != 4)) { | |
217 | return TCL_ERROR; | |
218 | } | |
219 | if (argc == 4) { | |
220 | if ((Tcl_GetInt(interp, argv[2], &graph->w_x) != TCL_OK) | |
221 | || (Tcl_GetInt(interp, argv[3], &graph->w_y) != TCL_OK)) { | |
222 | return TCL_ERROR; | |
223 | } | |
224 | } | |
225 | sprintf(interp->result, "%d %d", graph->w_x, graph->w_y); | |
226 | return TCL_OK; | |
227 | } | |
228 | ||
229 | ||
230 | int GraphCmdsize(GRAPH_ARGS) | |
231 | { | |
232 | if ((argc != 2) && (argc != 4)) { | |
233 | return TCL_ERROR; | |
234 | } | |
235 | if (argc == 4) { | |
236 | int w, h; | |
237 | ||
238 | if (Tcl_GetInt(interp, argv[2], &w) != TCL_OK) { | |
239 | return TCL_ERROR; | |
240 | } | |
241 | if (Tcl_GetInt(interp, argv[3], &h) != TCL_OK) { | |
242 | return TCL_ERROR; | |
243 | } | |
244 | graph->w_width = w; | |
245 | graph->w_height = h; | |
246 | } | |
247 | sprintf(interp->result, "%d %d", graph->w_width, graph->w_height); | |
248 | return TCL_OK; | |
249 | } | |
250 | ||
251 | ||
252 | int GraphCmdVisible(GRAPH_ARGS) | |
253 | { | |
254 | int visible; | |
255 | ||
256 | if ((argc != 2) && (argc != 3)) { | |
257 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); | |
258 | return TCL_ERROR; | |
259 | } | |
260 | ||
261 | if (argc == 3) { | |
262 | if ((Tcl_GetInt(interp, argv[2], &visible) != TCL_OK) || | |
263 | (visible < 0) || (visible > 1)) { | |
264 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); | |
265 | return TCL_ERROR; | |
266 | } | |
267 | ||
268 | graph->visible = visible; | |
269 | } | |
270 | ||
271 | sprintf(interp->result, "%d", graph->visible); | |
272 | ||
273 | return TCL_OK; | |
274 | } | |
275 | ||
276 | ||
277 | int GraphCmdRange(GRAPH_ARGS) | |
278 | { | |
279 | int range; | |
280 | ||
281 | if ((argc != 2) && (argc != 3)) { | |
282 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); | |
283 | return TCL_ERROR; | |
284 | } | |
285 | ||
286 | if (argc == 3) { | |
287 | if ((Tcl_GetInt(interp, argv[2], &range) != TCL_OK) || | |
288 | ((range != 10) && (range != 120))) { | |
289 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); | |
290 | return TCL_ERROR; | |
291 | } | |
292 | ||
293 | graph->range = range; | |
294 | NewGraph = 1; | |
295 | } | |
296 | ||
297 | sprintf(interp->result, "%d", graph->range); | |
298 | ||
299 | return TCL_OK; | |
300 | } | |
301 | ||
302 | ||
303 | int GraphCmdMask(GRAPH_ARGS) | |
304 | { | |
305 | int mask; | |
306 | ||
307 | if ((argc != 2) && (argc != 3)) { | |
308 | Tcl_AppendResult(interp, "wrong # args", (char *) NULL); | |
309 | return TCL_ERROR; | |
310 | } | |
311 | ||
312 | if (argc == 3) { | |
313 | if ((Tcl_GetInt(interp, argv[2], &mask) != TCL_OK) || | |
314 | (mask < 0) || (mask > 63)) { | |
315 | Tcl_AppendResult(interp, " bogus args", (char *) NULL); | |
316 | return TCL_ERROR; | |
317 | } | |
318 | ||
319 | graph->mask = mask; | |
320 | NewGraph = 1; | |
321 | } | |
322 | ||
323 | sprintf(interp->result, "%d", graph->mask); | |
324 | ||
325 | return TCL_OK; | |
326 | } | |
327 | ||
328 | ||
329 | int | |
330 | DoGraphCmd(CLIENT_ARGS) | |
331 | { | |
332 | SimGraph *graph = (SimGraph *) clientData; | |
333 | Tcl_HashEntry *ent; | |
334 | int result = TCL_OK; | |
335 | int (*cmd)(); | |
336 | ||
337 | if (argc < 2) { | |
338 | return TCL_ERROR; | |
339 | } | |
340 | ||
341 | if ((ent = Tcl_FindHashEntry(&GraphCmds, argv[1]))) { | |
342 | cmd = (int (*)())ent->clientData; | |
343 | Tk_Preserve((ClientData) graph); | |
344 | result = cmd(graph, interp, argc, argv); | |
345 | Tk_Release((ClientData) graph); | |
346 | } else { | |
347 | Tcl_AppendResult(interp, "unknown command name: \"", | |
348 | argv[0], " ", argv[1], "\".", (char *) NULL); | |
349 | result = TCL_ERROR; | |
350 | } | |
351 | return result; | |
352 | } | |
353 | ||
354 | ||
355 | int | |
356 | GraphViewCmd(CLIENT_ARGS) | |
357 | { | |
358 | SimGraph *graph; | |
359 | Tk_Window tkwin = (Tk_Window) clientData; | |
360 | ||
361 | if (argc < 2) { | |
362 | Tcl_AppendResult(interp, "wrong # args: should be \"", | |
363 | argv[0], " pathName ?options?\"", (char *) NULL); | |
364 | return TCL_ERROR; | |
365 | } | |
366 | ||
367 | tkwin = Tk_CreateWindowFromPath(interp, tkwin, | |
368 | argv[1], (char *) NULL); | |
369 | if (tkwin == NULL) { | |
370 | return TCL_ERROR; | |
371 | } | |
372 | ||
373 | graph = (SimGraph *)ckalloc(sizeof (SimGraph)); | |
374 | ||
375 | graph->tkwin = tkwin; | |
376 | graph->interp = interp; | |
377 | graph->flags = 0; | |
378 | ||
379 | Tk_SetClass(graph->tkwin, "GraphView"); | |
380 | Tk_CreateEventHandler(graph->tkwin, | |
381 | VisibilityChangeMask | | |
382 | ExposureMask | | |
383 | StructureNotifyMask, | |
384 | SimGraphEventProc, (ClientData) graph); | |
385 | Tcl_CreateCommand(interp, Tk_PathName(graph->tkwin), | |
386 | DoGraphCmd, (ClientData) graph, (void (*)()) NULL); | |
387 | ||
388 | /* | |
389 | Tk_MakeWindowExist(graph->tkwin); | |
390 | */ | |
391 | ||
392 | if (getenv("XSYNCHRONIZE") != NULL) { | |
393 | XSynchronize(Tk_Display(tkwin), 1); | |
394 | } | |
395 | ||
396 | InitNewGraph(graph); | |
397 | DoNewGraph(graph); | |
398 | ||
399 | if (ConfigureSimGraph(interp, graph, argc-2, argv+2, 0) != TCL_OK) { | |
400 | /* XXX: destroy graph */ | |
401 | Tk_DestroyWindow(graph->tkwin); | |
402 | return TCL_ERROR; | |
403 | } | |
404 | ||
405 | interp->result = Tk_PathName(graph->tkwin); | |
406 | return TCL_OK; | |
407 | } | |
408 | ||
409 | ||
410 | int | |
411 | ConfigureSimGraph(Tcl_Interp *interp, SimGraph *graph, | |
412 | int argc, char **argv, int flags) | |
413 | { | |
414 | if (Tk_ConfigureWidget(interp, graph->tkwin, GraphConfigSpecs, | |
415 | argc, argv, (char *) graph, flags) != TCL_OK) { | |
416 | return TCL_ERROR; | |
417 | } | |
418 | ||
419 | Tk_SetBackgroundFromBorder(graph->tkwin, graph->border); | |
420 | ||
421 | EventuallyRedrawGraph(graph); | |
422 | return TCL_OK; | |
423 | } | |
424 | ||
425 | ||
426 | ||
427 | ||
428 | char *HistName[] = { | |
429 | "Residential", "Commercial", "Industrial", | |
430 | "Cash Flow", "Crime", "Pollution" | |
431 | }; | |
432 | ||
433 | unsigned char HistColor[] = { | |
434 | COLOR_LIGHTGREEN, COLOR_DARKBLUE, COLOR_YELLOW, | |
435 | COLOR_DARKGREEN, COLOR_RED, COLOR_OLIVE | |
436 | }; | |
437 | ||
438 | ||
439 | void | |
440 | graph_command_init(void) | |
441 | { | |
442 | Tcl_CreateCommand(tk_mainInterp, "graphview", GraphViewCmd, | |
443 | (ClientData)MainWindow, (void (*)()) NULL); | |
444 | ||
445 | Tcl_InitHashTable(&GraphCmds, TCL_STRING_KEYS); | |
446 | ||
447 | #define GRAPH_CMD(name) HASHED_CMD(Graph, name) | |
448 | ||
449 | GRAPH_CMD(configure); | |
450 | GRAPH_CMD(position); | |
451 | GRAPH_CMD(size); | |
452 | GRAPH_CMD(Visible); | |
453 | GRAPH_CMD(Range); | |
454 | GRAPH_CMD(Mask); | |
455 | } | |
456 | ||
457 | ||
458 | ||
459 | void | |
460 | drawMonth(short *hist, unsigned char *s, float scale) | |
461 | { | |
462 | register short val; | |
463 | register short x; | |
464 | ||
465 | for (x = 0; x < 120; x++) { | |
466 | val = hist[x] * scale; | |
467 | if (val < 0) val = 0; | |
468 | if (val > 255) val = 255; | |
469 | s[119 - x] = val; | |
470 | } | |
471 | } | |
472 | ||
473 | ||
474 | void | |
475 | doAllGraphs(void) | |
476 | { | |
477 | float scaleValue; | |
478 | ||
479 | AllMax = 0; | |
480 | if (ResHisMax > AllMax) AllMax = ResHisMax; | |
481 | if (ComHisMax > AllMax) AllMax = ComHisMax; | |
482 | if (IndHisMax > AllMax) AllMax = IndHisMax; | |
483 | if (AllMax <= 128) AllMax = 0; | |
484 | ||
485 | if (AllMax) { | |
486 | scaleValue = 128.0 / AllMax; | |
487 | } else { | |
488 | scaleValue = 1.0; | |
489 | } | |
490 | ||
491 | // scaleValue = 0.5; // XXX | |
492 | ||
493 | drawMonth(ResHis, History10[RES_HIST], scaleValue); | |
494 | drawMonth(ComHis, History10[COM_HIST], scaleValue); | |
495 | drawMonth(IndHis, History10[IND_HIST], scaleValue); | |
496 | drawMonth(MoneyHis, History10[MONEY_HIST], 1.0); | |
497 | drawMonth(CrimeHis, History10[CRIME_HIST], 1.0); | |
498 | drawMonth(PollutionHis, History10[POLLUTION_HIST], 1.0); | |
499 | ||
500 | AllMax = 0; | |
501 | if (Res2HisMax > AllMax) AllMax = Res2HisMax; | |
502 | if (Com2HisMax > AllMax) AllMax = Com2HisMax; | |
503 | if (Ind2HisMax > AllMax) AllMax = Ind2HisMax; | |
504 | if (AllMax <= 128) AllMax = 0; | |
505 | ||
506 | if (AllMax) { | |
507 | scaleValue = 128.0 / AllMax; | |
508 | } else { | |
509 | scaleValue = 1.0; | |
510 | } | |
511 | ||
512 | // scaleValue = 0.5; // XXX | |
513 | ||
514 | drawMonth(ResHis + 120, History120[RES_HIST], scaleValue); | |
515 | drawMonth(ComHis + 120, History120[COM_HIST], scaleValue); | |
516 | drawMonth(IndHis + 120, History120[IND_HIST], scaleValue); | |
517 | drawMonth(MoneyHis + 120, History120[MONEY_HIST], 1.0); | |
518 | drawMonth(CrimeHis + 120, History120[CRIME_HIST], 1.0); | |
519 | drawMonth(PollutionHis + 120, History120[POLLUTION_HIST], 1.0); | |
520 | } | |
521 | ||
522 | ||
523 | void | |
524 | ChangeCensus(void) | |
525 | { | |
526 | CensusChanged = 1; | |
527 | } | |
528 | ||
529 | ||
530 | void | |
531 | graphDoer(void) | |
532 | { | |
533 | SimGraph *graph; | |
534 | ||
535 | if (CensusChanged) { | |
536 | doAllGraphs(); | |
537 | NewGraph = 1; | |
538 | CensusChanged = 0; | |
539 | } | |
540 | ||
541 | if (NewGraph) { | |
542 | for (graph = sim->graph; graph != NULL; graph = graph->next) { | |
543 | EventuallyRedrawGraph(graph); | |
544 | } | |
545 | NewGraph = 0; | |
546 | } | |
547 | } | |
548 | ||
549 | ||
550 | void | |
551 | initGraphs(void) | |
552 | { | |
553 | int i; | |
554 | SimGraph *graph; | |
555 | ||
556 | for (graph = sim->graph; graph != NULL; graph = graph->next) { | |
557 | graph->range = 10; | |
558 | graph->mask = ALL_HISTORIES; | |
559 | } | |
560 | ||
561 | if (!HistoryInitialized) { | |
562 | HistoryInitialized = 1; | |
563 | for (i = 0; i < HISTORIES; i++) { | |
564 | History10[i] = (unsigned char *)ckalloc(120); | |
565 | History120[i] = (unsigned char *)ckalloc(120); | |
566 | } | |
567 | } | |
568 | } | |
569 | ||
570 | ||
571 | /* comefrom: InitWillStuff */ | |
572 | void | |
573 | InitGraphMax(void) | |
574 | { | |
575 | register int x; | |
576 | ||
577 | ResHisMax = 0; | |
578 | ComHisMax = 0; | |
579 | IndHisMax = 0; | |
580 | for (x = 118; x >= 0; x--) { | |
581 | if (ResHis[x] > ResHisMax) ResHisMax = ResHis[x]; | |
582 | if (ComHis[x] > ComHisMax) ComHisMax = ComHis[x]; | |
583 | if (IndHis[x] > IndHisMax) IndHisMax = IndHis[x]; | |
584 | if (ResHis[x] < 0) ResHis[x] = 0; | |
585 | if (ComHis[x] < 0) ComHis[x] = 0; | |
586 | if (IndHis[x] < 0) IndHis[x] = 0; | |
587 | } | |
588 | Graph10Max = ResHisMax; | |
589 | if (ComHisMax > Graph10Max) Graph10Max = ComHisMax; | |
590 | if (IndHisMax > Graph10Max) Graph10Max = IndHisMax; | |
591 | ||
592 | Res2HisMax = 0; | |
593 | Com2HisMax = 0; | |
594 | Ind2HisMax = 0; | |
595 | for (x = 238; x >= 120; x--) { | |
596 | if (ResHis[x] > Res2HisMax) Res2HisMax = ResHis[x]; | |
597 | if (ComHis[x] > Com2HisMax) Com2HisMax = ComHis[x]; | |
598 | if (IndHis[x] > Ind2HisMax) Ind2HisMax = IndHis[x]; | |
599 | if (ResHis[x] < 0) ResHis[x] = 0; | |
600 | if (ComHis[x] < 0) ComHis[x] = 0; | |
601 | if (IndHis[x] < 0) IndHis[x] = 0; | |
602 | } | |
603 | Graph120Max = Res2HisMax; | |
604 | if (Com2HisMax > Graph120Max) Graph120Max = Com2HisMax; | |
605 | if (Ind2HisMax > Graph120Max) Graph120Max = Ind2HisMax; | |
606 | } | |
607 | ||
608 | ||
609 | void | |
610 | InitNewGraph(SimGraph *graph) | |
611 | { | |
612 | graph->next = NULL; | |
613 | graph->range = 10; | |
614 | graph->mask = ALL_HISTORIES; | |
615 | ||
616 | /* This stuff was initialized in our caller (GraphCmd) */ | |
617 | /* graph->tkwin = NULL; */ | |
618 | /* graph->interp = NULL; */ | |
619 | /* graph->flags = 0; */ | |
620 | ||
621 | graph->x = NULL; | |
622 | graph->visible = 0; | |
623 | graph->w_x = graph->w_y = 0; | |
624 | graph->w_width = graph->w_height = 0; | |
625 | graph->pixmap = None; | |
626 | graph->pixels = NULL; | |
627 | graph->fontPtr = NULL; | |
628 | graph->border = NULL; | |
629 | graph->borderWidth = 0; | |
630 | graph->relief = TK_RELIEF_FLAT; | |
631 | graph->draw_graph_token = 0; | |
632 | //fprintf(stderr, "InitNewGraph token %d\n", graph->draw_graph_token); | |
633 | ||
634 | graph->x = FindXDisplay(graph->tkwin); | |
635 | IncRefDisplay(graph->x); | |
636 | ||
637 | graph->pixels = graph->x->pixels; | |
638 | graph->fontPtr = NULL; | |
639 | ||
640 | DoResizeGraph(graph, 16, 16); | |
641 | } | |
642 | ||
643 | ||
644 | void | |
645 | DestroyGraph(SimGraph *graph) | |
646 | { | |
647 | SimGraph **gp; | |
648 | ||
649 | for (gp = &sim->graph; | |
650 | (*gp) != NULL; | |
651 | gp = &((*gp)->next)) { | |
652 | if ((*gp) == graph) { | |
653 | (*gp) = graph->next; | |
654 | sim->graphs--; | |
655 | break; | |
656 | } | |
657 | } | |
658 | ||
659 | if (graph->pixmap != None) { | |
660 | XFreePixmap(graph->x->dpy, graph->pixmap); | |
661 | graph->pixmap = None; | |
662 | } | |
663 | ||
664 | DecRefDisplay(graph->x); | |
665 | ||
666 | ckfree((char *) graph); | |
667 | } | |
668 | ||
669 | ||
670 | void | |
671 | DoResizeGraph(SimGraph *graph, int w, int h) | |
672 | { | |
673 | graph->w_width = w; graph->w_height = h; | |
674 | ||
675 | if (graph->pixmap != None) { | |
676 | XFreePixmap(graph->x->dpy, graph->pixmap); | |
677 | graph->pixmap = None; | |
678 | } | |
679 | graph->pixmap = XCreatePixmap(graph->x->dpy, graph->x->root, | |
680 | w, h, graph->x->depth); | |
681 | if (graph->pixmap == None) { | |
682 | fprintf(stderr, | |
683 | "Sorry, Micropolis can't create a pixmap on X display \"%s\".\n", | |
684 | graph->x->display); | |
685 | sim_exit(1); // Just sets tkMustExit and ExitReturn | |
686 | return; | |
687 | } | |
688 | } | |
689 | ||
690 | ||
691 | void | |
692 | DoNewGraph(SimGraph *graph) | |
693 | { | |
694 | sim->graphs++; graph->next = sim->graph; sim->graph = graph; | |
695 | ||
696 | NewGraph = 1; | |
697 | } | |
698 | ||
699 | ||
700 | #define BORDER 5 | |
701 | ||
702 | void | |
703 | DoUpdateGraph(SimGraph *graph) | |
704 | { | |
705 | Display *dpy; | |
706 | GC gc; | |
707 | Pixmap pm; | |
708 | int *pix; | |
709 | unsigned char **hist; | |
710 | int w, h, mask, i, j, x, y; | |
711 | XPoint points[121]; | |
712 | int year = (CityTime / 48) + StartingYear; | |
713 | int month = (CityTime / 4) % 12; | |
714 | int do_top_labels = 0; | |
715 | int do_right_labels = 0; | |
716 | int top_label_height = 30; | |
717 | int right_label_width = 65; | |
718 | int tx, ty; | |
719 | float sx, sy; | |
720 | ||
721 | if (!graph->visible) { | |
722 | return; | |
723 | } | |
724 | ||
725 | if (graph->range == 10) { | |
726 | hist = History10; | |
727 | } else { | |
728 | hist = History120; | |
729 | } | |
730 | ||
731 | dpy = graph->x->dpy; | |
732 | gc = graph->x->gc; | |
733 | pm = graph->pixmap; | |
734 | pix = graph->pixels; | |
735 | ||
736 | w = graph->w_width; | |
737 | h = graph->w_height; | |
738 | ||
739 | XSetFont(graph->x->dpy, graph->x->gc, graph->fontPtr->fid); | |
740 | XSetLineAttributes(dpy, gc, 3, LineSolid, CapButt, JoinBevel); | |
741 | if (graph->x->color) { | |
742 | XSetForeground(dpy, gc, pix[COLOR_LIGHTGRAY]); | |
743 | } else { | |
744 | XSetForeground(dpy, gc, pix[COLOR_WHITE]); | |
745 | } | |
746 | XFillRectangle(dpy, pm, gc, 0, 0, w, h); | |
747 | ||
748 | tx = BORDER; ty = BORDER; | |
749 | ||
750 | if ((w -= (2 * BORDER)) < 1) w = 1; | |
751 | if ((h -= (2 * BORDER)) < 1) h = 1; | |
752 | ||
753 | if (w > (4 * right_label_width)) { | |
754 | w -= right_label_width; | |
755 | do_right_labels = 1; | |
756 | } | |
757 | ||
758 | if (do_right_labels && | |
759 | (h > (3 * top_label_height))) { | |
760 | ty += top_label_height; | |
761 | h -= top_label_height; | |
762 | do_top_labels = 1; | |
763 | } | |
764 | ||
765 | sx = ((float)w) / 120.0; sy = ((float)h) / 256.0; | |
766 | ||
767 | mask = graph->mask; | |
768 | for (i = 0; i < HISTORIES; i++, mask >>= 1, hist++) { | |
769 | if (mask & 1) { | |
770 | int fg = COLOR_WHITE; | |
771 | int bg = COLOR_BLACK; | |
772 | Pixmap stipple = None; | |
773 | ||
774 | for (j = 0; j < 120; j++) { | |
775 | x = tx + (j * sx); | |
776 | y = ty + ((int)(h - (((float)(*hist)[j]) * sy))); | |
777 | points[j].x = x; points[j].y = y; | |
778 | } | |
779 | x = tx + (j * sx); | |
780 | points[j].x = x; points[j].y = y; | |
781 | ||
782 | if (graph->x->color) { | |
783 | XSetForeground(dpy, gc, pix[HistColor[i]]); | |
784 | } else { | |
785 | switch (i) { | |
786 | case 0: /* res */ | |
787 | stipple = graph->x->gray50_stipple; | |
788 | break; | |
789 | case 1: /* com */ | |
790 | stipple = graph->x->gray25_stipple; | |
791 | break; | |
792 | case 2: /* ind */ | |
793 | stipple = graph->x->gray75_stipple; | |
794 | break; | |
795 | case 3: /* cash */ | |
796 | fg = COLOR_BLACK; | |
797 | break; | |
798 | case 4: /* crime */ | |
799 | stipple = graph->x->horiz_stipple; | |
800 | break; | |
801 | case 5: /* pol */ | |
802 | stipple = graph->x->vert_stipple; | |
803 | break; | |
804 | } | |
805 | if (stipple != None) { | |
806 | XSetStipple(graph->x->dpy, gc, stipple); | |
807 | XSetTSOrigin(graph->x->dpy, gc, 0, 0); | |
808 | XSetForeground(graph->x->dpy, gc, pix[fg]); | |
809 | XSetBackground(graph->x->dpy, gc, pix[bg]); | |
810 | XSetFillStyle(graph->x->dpy, gc, FillOpaqueStippled); | |
811 | } else { | |
812 | XSetForeground(graph->x->dpy, gc, pix[fg]); | |
813 | } | |
814 | } | |
815 | ||
816 | XDrawLines(dpy, pm, gc, points, 121, CoordModeOrigin); | |
817 | ||
818 | if (!graph->x->color && (stipple != None)) { | |
819 | XSetFillStyle(graph->x->dpy, gc, FillSolid); | |
820 | } | |
821 | ||
822 | if (do_right_labels) { | |
823 | if (graph->x->color) { | |
824 | XSetForeground(dpy, gc, pix[HistColor[i]]); | |
825 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
826 | x + 4, y + 5, | |
827 | HistName[i], strlen(HistName[i])); | |
828 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
829 | x + 5, y + 4, | |
830 | HistName[i], strlen(HistName[i])); | |
831 | ||
832 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); | |
833 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
834 | x + 5, y + 5, | |
835 | HistName[i], strlen(HistName[i])); | |
836 | } else { | |
837 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); | |
838 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
839 | x + 5, y + 5, | |
840 | HistName[i], strlen(HistName[i])); | |
841 | } | |
842 | } | |
843 | } | |
844 | } | |
845 | ||
846 | XSetLineAttributes(dpy, gc, 1, LineSolid, CapButt, JoinMiter); | |
847 | ||
848 | XSetForeground(dpy, gc, pix[COLOR_BLACK]); | |
849 | XDrawLine(dpy, pm, gc, tx, ty - 1, tx + w, ty - 1); | |
850 | XDrawLine(dpy, pm, gc, tx, ty + h, tx + w, ty + h); | |
851 | ||
852 | if (graph->range == 10) { | |
853 | for (x = 120 - month; x >= 0; x -= 12) { | |
854 | int xx, yy; | |
855 | xx = tx + (x * sx); | |
856 | XDrawLine(dpy, pm, gc, xx, ty - 1, xx, ty + h); | |
857 | if (do_top_labels) { | |
858 | char buf[256]; | |
859 | ||
860 | sprintf(buf, "%d", year--); | |
861 | xx = tx + (x * sx) + 2; | |
862 | yy = ty - ((year & 1) ? 4 : 20); | |
863 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
864 | xx, yy, buf, strlen(buf)); | |
865 | } | |
866 | } | |
867 | } else { | |
868 | int past; | |
869 | ||
870 | sx /= 10; | |
871 | past = 10 * (year % 10); | |
872 | year /= 10; | |
873 | ||
874 | for (x = 1200 - past; x >= 0; x -= 120) { | |
875 | int xx, yy; | |
876 | xx = tx + (x * sx); | |
877 | XDrawLine(dpy, pm, gc, xx, ty - 1, xx, ty + h); | |
878 | if (do_top_labels) { | |
879 | char buf[256]; | |
880 | ||
881 | sprintf(buf, "%d0", year--); | |
882 | ||
883 | xx = tx + (x * sx) + 2; | |
884 | yy = ty - ((year & 1) ? 4 : 20); | |
885 | XDrawString(graph->x->dpy, pm, graph->x->gc, | |
886 | xx, yy, buf, strlen(buf)); | |
887 | } | |
888 | } | |
889 | } | |
890 | ||
891 | XCopyArea(graph->x->dpy, graph->pixmap, | |
892 | Tk_WindowId(graph->tkwin), graph->x->gc, | |
893 | 0, 0, graph->w_width, graph->w_height, 0, 0); | |
894 | } | |
895 | ||
896 |