|
|
1.1 root 1: #include <X/mit-copyright.h>
2:
3: /* Copyright 1985, Massachusetts Institute of Technology */
4:
5: #ifndef lint
6: static char *rcsid_bitmap_c = "$Header: bitmap.c,v 10.9 86/11/19 19:13:23 jg Rel $";
7: #endif
8:
9: #include <errno.h>
10: #include <stdio.h>
11: #include <X/Xlib.h>
12: #include <sys/types.h>
13:
14: #include "../cursors/cross.cursor"
15: #include "../cursors/cross_mask.cursor"
16: #include "../cursors/ul_angle.cursor"
17: #include "../cursors/ul_angle_mask.cursor"
18: #include "../cursors/lr_angle.cursor"
19: #include "../cursors/lr_angle_mask.cursor"
20: #include "../cursors/dot.cursor"
21: #include "../cursors/dot_mask.cursor"
22:
23: #define TOP_MARGIN 10
24: #define LEFT_MARGIN 10
25: #define BOTTOM_MARGIN 10
26: #define AROUND_RASTER_MARGIN 20
27: #define GRID_TO_COMMAND_MARGIN 5
28: #define RIGHT_MARGIN 5
29:
30: #define MIN_SQUARE_SIZE 3
31: #define DEFAULT_SQUARE_SIZE 13
32:
33: #define bit int
34: #define boolean int
35: #define TRUE 1
36: #define FALSE 0
37: #define OUT_OF_RANGE 10000
38:
39: #define COPY 0
40: #define MOVE 1
41: #define OVERLAY 2
42:
43: #define min(x,y) ((x < y) ? x : y)
44: #define max(x,y) ((x < y) ? y : x)
45:
46: /* error handling stuff */
47: extern int errno;
48: extern char *sys_errlist[];
49:
50: /* global "constants" -- set once at startup time */
51: /* the first few variables are not static because they are shared
52: with dialog.c */
53: int foreground = BlackPixel;
54: int background = WhitePixel;
55: Pixmap backmap;
56: Pixmap border;
57: int borderwidth = 3;
58: int invertplane = 1;
59: int highlightplane = 1;
60: int mousepix = BlackPixel;
61: static int squares_wide = OUT_OF_RANGE;
62: static int squares_high = OUT_OF_RANGE;
63: static short *raster;
64: static int raster_length; /* how many shorts in the raster[] array */
65: static Window outer_window, grid_window;
66: static Window raster_window, raster_invert_window;
67: static Font font;
68: static FontInfo fontInfo;
69: static Cursor cross, upper_left, lower_right, dot;
70: static char *filename = NULL; /* name of input file */
71: static char *backup_filename;
72: static char *stripped_name;
73: /* file name without directory path or extension */
74: static char *progname; /* name this program was invoked by */
75: static Pattern DottedPattern = XMakePattern (
76: 1 /* pattern */,
77: 2 /* length */,
78: 1); /* multiplier */
79: static Pattern InverseDottedPattern = XMakePattern (
80: 2 /* pattern */,
81: 2 /* length */,
82: 1); /* multiplier */
83:
84: /* command-button data */
85: #define N_COMMANDS 16
86: static struct command_data {
87: Window window;
88: char *name;
89: int name_length;
90: int x_offset; /* so text is centered within command box */
91: boolean inverted;
92: int (*proc)();
93: /* function to invoke when command button is "pressed" */
94: /* actually no return value, but compiler doesn't like "void" here */
95: int data; /* arbitrary instance data to call procedure back with */
96: } commands [N_COMMANDS];
97:
98: /* global variables */
99: /* layout-related variables */
100: static int square_size; /* length of square's side, in pixels */
101: static OpaqueFrame frames[N_COMMANDS+3], outer_frame;
102: /* frames[0] throgh frames[N_COMMANDS-1] are the command windows;
103: frames[N_COMMANDS] is the raster;
104: frames[N_COMMANDS+1] is the inverted raster;
105: frames[N_COMMANDS+2] is the grid */
106:
107: /* location of x'd-through squares, if any */
108: static int x1_square_exed_through = OUT_OF_RANGE;
109: static int y1_square_exed_through = OUT_OF_RANGE;
110: static int x2_square_exed_through = OUT_OF_RANGE;
111: static int y2_square_exed_through = OUT_OF_RANGE;
112:
113: /* location of "plus'd through" squares, if any */
114: static int x1_square_plus_through = OUT_OF_RANGE;
115: static int y1_square_plus_through = OUT_OF_RANGE;
116: static int x2_square_plus_through = OUT_OF_RANGE;
117: static int y2_square_plus_through = OUT_OF_RANGE;
118:
119: /* location of hot spot, if any */
120: static int x_hot_spot = OUT_OF_RANGE;
121: static int y_hot_spot = OUT_OF_RANGE;
122:
123: static boolean changed = FALSE;
124: /* has user changed bitmap since starting program or last write? */
125:
126: static enum RepaintGridType {e_AgainstBackground, e_AgainstForeground, e_Invert};
127:
128: extern char *malloc();
129:
130: main (argc, argv)
131: int argc;
132: char **argv;
133: {
134: SetUp (argc, argv);
135: while (TRUE) {
136: XEvent event;
137: XNextEvent(&event);
138: ProcessEvent(&event);
139: }
140: } /* end of main procedure */
141:
142:
143: SetUp (argc, argv)
144: int argc;
145: char **argv;
146: {
147: char *StripName(), *BackupName(), *index();
148: char *option;
149: FILE *file;
150: char *geometry = NULL, *host = NULL, *dimensions = NULL;
151: int i;
152:
153: progname = argv[0];
154: setlinebuf (stderr);
155:
156: /* Parse command line */
157: for (i = 1; i < argc; i++) {
158: if (argv[i][0] == '=')
159: geometry = argv[i];
160:
161: else if (index (argv[i], ':') != NULL)
162: host = argv[i];
163:
164: else if (filename == NULL)
165: filename = argv[i];
166:
167: else
168: dimensions = argv[i];
169: }
170:
171: if (filename == NULL) {
172: fprintf (stderr, "%s: no file name specified\n", progname);
173: exit (1);
174: }
175:
176: stripped_name = StripName (filename);
177: backup_filename = BackupName (filename);
178:
179: file = fopen (filename, "r");
180: if (!file && (errno != ENOENT)) {
181: fprintf (stderr, "%s: could not open file '%s' for reading -- %s\n",
182: progname, filename, sys_errlist[errno]);
183: exit (1);
184: }
185:
186: if (file)
187: DimensionsFromFile(file);
188: else {
189: if (dimensions != NULL) {
190: if (sscanf (dimensions, "%dx%d", &squares_wide, &squares_high) != 2) {
191: fprintf (stderr, "%s: invalid dimensions '%s'\n",
192: progname, dimensions);
193: exit (1);
194: }
195: if ((squares_wide <=0) || (squares_high <=0)) {
196: fprintf (stderr, "%s: dimensions must be positive\n", progname);
197: exit (1);
198: }
199: }
200: else /* dimensions not supplied on command line */
201: squares_wide = squares_high = 16;
202: }
203:
204: raster_length = ((squares_wide+15)/16)*squares_high;
205: raster = (short *) malloc (raster_length*sizeof(short));
206:
207: if (file) {
208: InitialValuesFromFile(file);
209: fclose (file);
210: }
211: else {
212: /* set raster to all 0's (background color) */
213: register int i;
214: for (i=0;i<raster_length;i++)
215: raster[i] = 0;
216: }
217:
218: if (!XOpenDisplay(host)) {
219: fprintf(stderr, "%s: Can't open display '%s'\n",
220: argv[0], XDisplayName(host));
221: exit (1);
222: }
223:
224: backmap = WhitePixmap;
225: border = BlackPixmap;
226: if ((option = XGetDefault(progname, "BorderWidth")) != NULL)
227: borderwidth = atoi(option);
228: if ((option = XGetDefault(progname, "BodyFont")) == NULL)
229: option = "vtsingle";
230: font = XGetFont (option);
231: XQueryFont (font, &fontInfo);
232: if (DisplayCells() > 2) {
233: char *fore_color = XGetDefault(progname, "Foreground");
234: char *back_color = XGetDefault(progname, "Background");
235: char *high_color = XGetDefault(progname, "Highlight");
236: char *brdr_color = XGetDefault(progname, "Border");
237: char *mous_color = XGetDefault(progname, "Mouse");
238: Color fdef, bdef, hdef;
239: if (fore_color && XParseColor(fore_color, &fdef) &&
240: back_color && XParseColor(back_color, &bdef) &&
241: (high_color == NULL || XParseColor(high_color, &hdef)) &&
242: XGetColorCells(0, 1, high_color ? 2 : 1, &invertplane, &bdef.pixel)) {
243: background = bdef.pixel;
244: backmap = XMakeTile(background);
245: if (high_color) {
246: hdef.pixel = bdef.pixel | invertplane;
247: XStoreColor(&hdef);
248: highlightplane = 1 << (ffs(invertplane) - 1);
249: hdef.pixel = bdef.pixel | highlightplane;
250: XStoreColor(&hdef);
251: invertplane ^= highlightplane;
252: } else
253: highlightplane = invertplane;
254: XStoreColor(&bdef);
255: foreground = background | invertplane;
256: fdef.pixel = foreground;
257: XStoreColor(&fdef);
258: }
259: if (brdr_color && XParseColor(brdr_color, &bdef) &&
260: XGetHardwareColor(&bdef))
261: border = XMakeTile(bdef.pixel);
262: if (mous_color && XParseColor(mous_color, &fdef) &&
263: XGetHardwareColor(&fdef))
264: mousepix = fdef.pixel;
265: }
266:
267: {
268: int right_side_bottom, right_side_width;
269: int minwidth, minheight;
270: int default_width, default_height, default_x, default_y;
271: int display_width = DisplayWidth();
272: int display_height = DisplayHeight();
273: char default_geometry[20];
274:
275: LayoutStage1();
276: right_side_bottom = frames[N_COMMANDS+1].y + frames[N_COMMANDS+1].height
277: + 2 /* borders */ + AROUND_RASTER_MARGIN;
278: right_side_width = frames[0].width + 2 /* borders */
279: + GRID_TO_COMMAND_MARGIN + RIGHT_MARGIN;
280: OuterWindowDims (MIN_SQUARE_SIZE, right_side_width, right_side_bottom,
281: &minwidth, &minheight);
282: OuterWindowDims (DEFAULT_SQUARE_SIZE, right_side_width, right_side_bottom,
283: &default_width, &default_height);
284: default_x = min (200, display_width - default_width - 2*borderwidth);
285: default_y = min (200, display_height - default_height - 2*borderwidth);
286: sprintf (default_geometry, "=%dx%d+%d+%d", default_width,
287: default_height, default_x, default_y);
288: outer_frame.bdrwidth = borderwidth;
289: outer_frame.border = border;
290: outer_frame.background = backmap;
291: outer_window = XCreate ("Bitmap Editor", progname, geometry,
292: default_geometry, &outer_frame, minwidth, minheight);
293: LayoutStage2 ();
294: }
295:
296: upper_left = XCreateCursor (ul_angle_width, ul_angle_height,
297: ul_angle_bits, ul_angle_mask_bits, ul_angle_x_hot, ul_angle_y_hot,
298: mousepix, background, GXcopy);
299: lower_right = XCreateCursor (lr_angle_width, lr_angle_height,
300: lr_angle_bits, lr_angle_mask_bits,
301: lr_angle_x_hot, lr_angle_y_hot, mousepix, background, GXcopy);
302: cross = XCreateCursor (cross_width, cross_height, cross_bits,
303: cross_mask_bits, cross_x_hot, cross_y_hot, mousepix, background,
304: GXcopy);
305: dot = XCreateCursor (dot_width, dot_height, dot_bits,
306: dot_mask_bits, dot_x_hot, dot_y_hot, mousepix, background,
307: GXcopy);
308: XDefineCursor (outer_window, cross);
309:
310: XExpandEvents(); /* do NOT collapse adjacent MouseMotion events */
311: XSelectInput (outer_window, ExposeWindow); /* to detect size changes */
312: XMapWindow (outer_window);
313: XMapSubwindows (outer_window);
314: } /* end of Set_Up procedure */
315:
316:
317: /* Unfortunately, the current implementation of X (version 10) does not
318: handle resize event notification very well. When the outer window
319: is resized, X sends ExposeWindow events for each subwindow BEFORE sending
320: an ExposeWindow for the outer window.
321:
322: If I handled the events in the order sent, I would repaint each
323: subwindow, only then to discover that the outer window had changed
324: size...at which time I would unmap and rearrange the subwindows,
325: causing ANOTHER set of exposure events on the subwindows. This would
326: not just be inefficient, it would also look ugly on the screen.
327:
328: To work around this misfeature, I do not process ExposeWindow events
329: immediately upon receipt, but instead go into a recursion (HandleExposure)
330: until I either run out of events or get a non-ExposeWindow event. If
331: the list of ExposeWindow events ends with a resize, I discard all of the
332: earlier ExposeWindow events. Otherwise, I unwind out of the recursion,
333: processing each exposure event in the opposite of the order received,
334: eventually returning to the normal event notification loop.
335:
336: This code is admittedly convoluted, and will hopefully go away in a
337: future version of X. */
338:
339: #define PROCESS_IT 0
340: #define DISCARD_IT 1
341:
342: ProcessEvent (event)
343: register XEvent *event;
344: {
345: register Window w = event->window;
346: register int i;
347: if (event->type == ExposeWindow) {
348: int status;
349: /* make sure that I get all the exposure events that have been sent */
350: XSync(0);
351: status = HandleExposure (event);
352: if (status == DISCARD_IT)
353: return;
354: }
355: ProcessEventReally (event);
356: }
357:
358: ProcessEventReally (event)
359: register XEvent *event;
360: {
361: register Window w = event->window;
362: register int i;
363: if (w == grid_window)
364: ProcessGridWindowEvent (event);
365: else if (w == outer_window)
366: ProcessOuterWindowEvent (event);
367: else if (w == raster_window)
368: RepaintRaster();
369: else if (w == raster_invert_window)
370: RepaintRasterInverted();
371: else for (i=0;i<N_COMMANDS;i++)
372: if (w == commands[i].window)
373: ProcessCommandButtonEvent (&commands[i], event);
374: } /* end of ProcessInput procedure */
375:
376:
377: int HandleExposure (event)
378: XExposeWindowEvent *event;
379: {
380: int status;
381: XEvent next_event;
382:
383: if (QLength() == 0)
384: /* there are no more events, so this can't be a resize */
385: return (PROCESS_IT);
386:
387: XNextEvent (&next_event);
388: if (next_event.type != ExposeWindow) {
389: /* the list of exposures ended, so this can't be a resize */
390: XPutBackEvent (&next_event);
391: return (PROCESS_IT);
392: }
393:
394: if (next_event.window == outer_window) {
395: if ((outer_frame.height == ((XExposeWindowEvent *)&next_event)->height)
396: && (outer_frame.width == ((XExposeWindowEvent *)&next_event)->width))
397: /* the list of exposures ended with a non-resize */
398: status = PROCESS_IT;
399: else
400: /* this IS a resize */
401: status = DISCARD_IT;
402: /* Handle the outer window exposure, whether or not it's a resize */
403: ProcessEventReally (&next_event);
404: return (status);
405: }
406:
407: if ((status = HandleExposure (&next_event)) == PROCESS_IT)
408: ProcessEventReally (&next_event);
409: return (status);
410: }
411:
412: ProcessGridWindowEvent (event)
413: XEvent *event;
414: {
415: int x_square, y_square;
416: static int x_square_prev, y_square_prev;
417: static boolean raster_outdated;
418: switch (event->type) {
419:
420: case ExposeWindow:
421: RepaintGridLines(e_AgainstBackground);
422: RefillGrid(FALSE);
423: if (x1_square_exed_through != OUT_OF_RANGE)
424: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through,
425: x2_square_exed_through, y2_square_exed_through);
426: if (x1_square_plus_through != OUT_OF_RANGE)
427: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
428: x2_square_plus_through, y2_square_plus_through);
429: if (x_hot_spot != OUT_OF_RANGE)
430: HighlightHotSpot();
431: break;
432:
433: case ExposeRegion: {
434: #define this_event ((XExposeRegionEvent *)event)
435: int x1 = this_event->x;
436: int y1 = this_event->y;
437: int x2 = x1 + this_event->width;
438: int y2 = y1 + this_event->height;
439: #undef this_event
440: x1 /= square_size;
441: x2 /= square_size;
442: y1 /= square_size;
443: y2 /= square_size;
444: if (x2 >= squares_wide)
445: x2 = squares_wide - 1; /* sanity check */
446: if (y2 >= squares_high)
447: y2 = squares_high - 1; /* sanity check */
448: RepaintGridLinesPartially(x1,y1,x2+1,y2+1,e_AgainstBackground,TRUE);
449: RefillGridPartially (x1,y1,x2,y2,FALSE);
450: if (x1_square_exed_through != OUT_OF_RANGE)
451: ExThroughRectangle (
452: max (x1, x1_square_exed_through),
453: max (y1, y1_square_exed_through),
454: min (x2, x2_square_exed_through),
455: min (y2, y2_square_exed_through));
456: if (x1_square_plus_through != OUT_OF_RANGE)
457: PlusThroughRectangle (
458: max (x1, x1_square_plus_through),
459: max (y1, y1_square_plus_through),
460: min (x2, x2_square_plus_through),
461: min (y2, y2_square_plus_through));
462: if (x_hot_spot >= x1 && x_hot_spot <= x2
463: && y_hot_spot >= y1 && y_hot_spot <= y2)
464: HighlightHotSpot();
465: break;
466: }
467:
468: case ButtonPressed:
469: if (WhatSquare (event, &x_square, &y_square))
470: return; /* mouse outside grid; really shouldn't happen, but... */
471: switch (((XButtonPressedEvent *)event)->detail & ValueMask) {
472: case LeftButton:
473: PaintSquare (x_square, y_square, foreground);
474: if (x_square == x_hot_spot && y_square == y_hot_spot)
475: HighlightHotSpot();
476: SetRasterBit (raster, x_square, y_square, 1);
477: break;
478: case MiddleButton:
479: InvertSquare (x_square, y_square);
480: InvertRasterBit (raster, x_square, y_square);
481: break;
482: case RightButton:
483: PaintSquare (x_square, y_square, background);
484: if (x_square == x_hot_spot && y_square == y_hot_spot)
485: HighlightHotSpot();
486: SetRasterBit (raster, x_square, y_square, 0);
487: break;
488: }
489: RepaintRaster();
490: RepaintRasterInverted();
491: x_square_prev = x_square;
492: y_square_prev = y_square;
493: raster_outdated = FALSE;
494: changed = TRUE;
495: break;
496:
497: case MouseMoved:
498: if (WhatSquare (event, &x_square, &y_square))
499: return; /* mouse outside grid; really shouldn't happen, but... */
500: if ((x_square != x_square_prev) || (y_square != y_square_prev))
501: switch (((XMouseMovedEvent *)event)->detail) {
502: case LeftMask:
503: PaintSquare (x_square, y_square, foreground);
504: if (x_square == x_hot_spot && y_square == y_hot_spot)
505: HighlightHotSpot();
506: SetRasterBit (raster, x_square, y_square, 1);
507: changed = raster_outdated = TRUE;
508: break;
509: case MiddleMask:
510: InvertSquare (x_square, y_square);
511: InvertRasterBit (raster, x_square, y_square);
512: changed = raster_outdated = TRUE;
513: break;
514: case RightMask:
515: PaintSquare (x_square, y_square, background);
516: if (x_square == x_hot_spot && y_square == y_hot_spot)
517: HighlightHotSpot();
518: SetRasterBit (raster, x_square, y_square, 0);
519: changed = raster_outdated = TRUE;
520: break;
521: default:
522: break; /* ignore events with multiple buttons down */
523: }
524: if (raster_outdated && !MouseMovedEventQueued()) {
525: RepaintRaster();
526: RepaintRasterInverted();
527: raster_outdated = FALSE;
528: }
529: x_square_prev = x_square;
530: y_square_prev = y_square;
531: break;
532:
533: }
534: } /* end of ProcessGridWindowEvent procedure */
535:
536: boolean MouseMovedEventQueued () {
537: XEvent event;
538: if (XPending() == 0) return (FALSE);
539: XPeekEvent (&event);
540: return (event.type == MouseMoved);
541: }
542:
543:
544: ProcessOuterWindowEvent (event)
545: XEvent *event;
546: {
547: if (event->type != ExposeWindow)
548: return;
549: if ((outer_frame.height == ((XExposeWindowEvent *)event)->height)
550: && (outer_frame.width == ((XExposeWindowEvent *)event)->width))
551: /* if this isn't a resize, there's nothing to do here. */
552: return;
553:
554: /* the outer window's size has changed. Must rearrange subwindows. */
555: outer_frame.height = ((XExposeWindowEvent *)event)->height;
556: outer_frame.width = ((XExposeWindowEvent *)event)->width;
557: XDestroySubwindows (outer_window);
558: LayoutStage2 ();
559: XMapSubwindows (outer_window);
560: }
561:
562: ProcessCommandButtonEvent (command, event)
563: struct command_data *command;
564: XEvent *event;
565: {
566: static struct command_data *button_down_command;
567:
568: switch (event->type) {
569:
570: case ExposeWindow:
571: if (command->inverted)
572: XClear (command->window);
573: XTextMask (
574: command->window, /* w */
575: command->x_offset, /* x */
576: 0, /* y */
577: command->name, /* string */
578: command->name_length, /* length */
579: font, /* font */
580: foreground); /* source pixel */
581: if (command->inverted)
582: InvertCommandWindow (command);
583: break;
584:
585: case ButtonPressed:
586: if (button_down_command != NULL)
587: break; /* must be a second button push--ignore */
588: button_down_command = command;
589: InvertCommandWindow (command);
590: command->inverted = TRUE;
591: break;
592:
593: case LeaveWindow:
594: if (command == button_down_command) {
595: InvertCommandWindow (command);
596: command->inverted = FALSE;
597: button_down_command = NULL;
598: }
599: break;
600:
601: case ButtonReleased:
602: if (command == button_down_command) {
603: (*command->proc)(command->data);
604: button_down_command = NULL;
605: InvertCommandWindow (command);
606: command->inverted = FALSE;
607: }
608: break;
609:
610: }
611: }
612:
613:
614: InvertCommandWindow (command)
615: struct command_data *command;
616: {
617: XPixFill (
618: command->window,
619: 0, /* x */
620: 0, /* y */
621: 400, /* width = "infinity " */
622: fontInfo.height, /* height */
623: 1, /* pixel */
624: NULL, /* clipmask bitmap */
625: GXinvert, /* function */
626: invertplane); /* planes */
627: } /* end of InvertCommandWindow procedure */
628:
629:
630: /* WhatSquare returns TRUE if mouse is outside grid, FALSE if inside.
631: If it returns FALSE, it assigns to *x_square and *y_square. */
632:
633: boolean WhatSquare (event, x_square, y_square)
634: register XEvent *event;
635: register int *x_square, *y_square; /*RETURN*/
636: {
637: int x = ((XKeyPressedEvent *)event)->x;
638: int y = ((XKeyPressedEvent *)event)->y;
639: if ((x < 0) || (y < 0))
640: return (TRUE);
641: *x_square = x/square_size;
642: *y_square = y/square_size;
643: return ((*x_square >= squares_wide) || (*y_square >= squares_high));
644: } /* end of WhatSquare procedure */
645:
646:
647: RepaintGridLines(how)
648: enum RepaintGridType how;
649: {
650: RepaintGridLinesPartially (0, 0, squares_wide, squares_high, how, TRUE);
651: }
652:
653: RepaintGridLinesPartially (x1, y1, x2, y2, how, include_boundaries)
654: int x1, y1, x2, y2;
655: enum RepaintGridType how;
656: boolean include_boundaries;
657: {
658: register int i;
659: Vertex v[2];
660: int pixel, func, planes;
661: Pattern pattern;
662: switch (how) {
663: case e_AgainstBackground:
664: pixel = foreground;
665: pattern = DottedPattern;
666: func = GXcopy;
667: planes = AllPlanes;
668: break;
669: case e_AgainstForeground:
670: pixel = background;
671: pattern = InverseDottedPattern;
672: func = GXcopy;
673: planes = AllPlanes;
674: break;
675: case e_Invert:
676: pixel = 1;
677: pattern = SolidLine;
678: func = GXinvert;
679: planes = invertplane;
680: break;
681: }
682:
683: /* draw vertical grid lines */
684: v[0].flags = v[1].flags = 0;
685: v[0].y = y1*square_size;
686: v[0].y += (v[0].y & 1); /* make sure pattern is aligned on even bit boundary */
687: v[1].y = y2*square_size;
688: if (!include_boundaries) {x1++;x2--;}
689: v[0].x = v[1].x = x1*square_size;
690: for (i=x1;i<=x2; i++) {
691: XDrawDashed (
692: grid_window,
693: v, /* vertices */
694: 2, /* vertex count */
695: 1, /* width */
696: 1, /* height */
697: pixel,
698: pattern,
699: func,
700: planes); /* planes */
701: v[0].x = (v[1].x += square_size);
702: }
703: if (!include_boundaries) {x1--;x2++;}
704:
705: /* draw horizontal grid lines */
706: v[0].flags = v[1].flags = 0;
707: v[0].x = x1*square_size;
708: v[0].x += (v[0].x & 1); /* make sure pattern is aligned on even bit boundary */
709: v[1].x = x2*square_size;
710: if (!include_boundaries) {y1++;y2--;}
711: v[0].y = v[1].y = y1*square_size;
712: for (i=y1;i<=y2;i++) {
713: XDrawDashed (
714: grid_window,
715: v, /* vertices */
716: 2, /* vertex count */
717: 1, /* width */
718: 1, /* height */
719: pixel,
720: pattern,
721: func,
722: planes); /* planes */
723: v[0].y = (v[1].y += square_size);
724: }
725: } /* end of RepaintGridLinesPartially procedure */
726:
727:
728: RefillGrid (paint_background)
729: boolean paint_background;
730: {
731: RefillGridPartially (0, 0, squares_wide-1, squares_high-1, paint_background);
732: }
733:
734:
735: RefillGridPartially(x1, y1, x2, y2, paint_background)
736: register int x1, y1, x2, y2;
737: boolean paint_background;
738: {
739: register i, j;
740: for (i=x1; i<=x2; i++) {
741: for (j=y1; j<=y2; j++) {
742: bit b = GetRasterBit (raster, i, j);
743: if (b || paint_background)
744: PaintSquare (i, j, (b ? foreground : background));
745: }
746: }
747: } /* end of RefillGridPartially procedure */
748:
749:
750: PaintSquare(x, y, pixel)
751: int x, y;
752: int pixel; /* display function */
753: {
754: XPixSet (
755: grid_window,
756: x*square_size + 1, /* x */
757: y*square_size + 1, /* y */
758: square_size - 1, /* width */
759: square_size - 1, /* height */
760: pixel); /* pixel */
761: } /* end of PaintSquare procedure */
762:
763: InvertSquare(x, y)
764: int x, y;
765: {
766: XPixFill (
767: grid_window,
768: x*square_size + 1, /* x */
769: y*square_size + 1, /* y */
770: square_size - 1, /* width */
771: square_size - 1, /* height */
772: 1, /* pixel */
773: NULL, /* clipmask */
774: GXinvert, /* function */
775: invertplane); /* planes */
776: }
777:
778: bit GetRasterBit (raster, x, y)
779: short *raster;
780: register int x;
781: int y;
782: {
783: register short *word = raster + x/16 + y*((squares_wide+15)/16);
784: return ( (*word & (1 << (x % 16))) ? 1 : 0);
785: } /* end of GetRasterBit procedure */
786:
787:
788: SetRasterBit (raster, x, y, new)
789: short *raster;
790: register int x;
791: int y;
792: bit new;
793: {
794: register short *word = raster + x/16 + y*((squares_wide+15)/16);
795: x %= 16;
796: *word = (new << x) | (*word & ~(1 << x));
797: } /* end of SetRasterBit procedure */
798:
799:
800: InvertRasterBit (raster, x, y)
801: short *raster;
802: register int x;
803: int y;
804: {
805: register short *word = raster + x/16 + y*((squares_wide+15)/16);
806: *word ^= (1 << (x % 16));
807: } /* end of InvertRasterBit procedure */
808:
809:
810: RepaintRaster() {
811: XBitmapBitsPut (
812: raster_window,
813: 3, /* x */
814: 3, /* y */
815: squares_wide, /* width */
816: squares_high, /* height */
817: raster, /* data */
818: foreground, /* foreground */
819: background, /* background */
820: 0, /* mask */
821: GXcopy, /* func */
822: AllPlanes); /* planes */
823: } /* end of RepaintRaster procedure */
824:
825:
826: RepaintRasterInverted () {
827: XBitmapBitsPut (
828: raster_invert_window,
829: 3, /* x */
830: 3, /* y */
831: squares_wide, /* width */
832: squares_high, /* height */
833: raster, /* data */
834: background, /* foreground */
835: foreground, /* background */
836: 0, /* mask */
837: GXcopy, /* func */
838: AllPlanes); /* planes */
839: } /* end of RepaintRasterInverted procedure */
840:
841:
842: WriteOutputToFile (file)
843: FILE *file;
844: {
845: register int i;
846: fprintf (file, "#define %s_width %d\n", stripped_name, squares_wide);
847: fprintf (file, "#define %s_height %d\n", stripped_name, squares_high);
848: if (x_hot_spot != OUT_OF_RANGE)
849: fprintf (file, "#define %s_x_hot %d\n", stripped_name, x_hot_spot);
850: if (y_hot_spot != OUT_OF_RANGE)
851: fprintf (file, "#define %s_y_hot %d\n", stripped_name, y_hot_spot);
852: fprintf (file, "static short %s_bits[] = {\n 0x%04x",
853: stripped_name, (u_short) raster[0]);
854:
855: for (i=1;i<raster_length;i++) {
856: fprintf (file, ",");
857: fprintf (file, (i % 4) ? " " : "\n ");
858: fprintf (file, "0x%04x", (u_short) raster[i]);
859: }
860: fprintf (file, "};\n");
861: } /* end of WriteOutputToFile procedure */
862:
863:
864: DimensionsFromFile(file)
865: FILE *file;
866: {
867: char variable[81];
868: int value;
869: while (fscanf (file, "#define %80s %d\n", variable, &value) == 2) {
870: if (StringEndsWith (variable, "width"))
871: squares_wide = value;
872: else if (StringEndsWith (variable, "height"))
873: squares_high = value;
874: else if (StringEndsWith (variable, "x_hot"))
875: x_hot_spot = value;
876: else if (StringEndsWith (variable, "y_hot"))
877: y_hot_spot = value;
878: else fprintf (stderr, "Unrecognized variable '%s' in file '%s'\n",
879: variable, filename);
880: }
881:
882: if (squares_wide <= 0 || squares_wide == OUT_OF_RANGE) {
883: fprintf (stderr,
884: "%s: file '%s' does not have a valid width dimension\n",
885: progname, filename);
886: exit(1);
887: }
888:
889: if (squares_high <= 0 || squares_high == OUT_OF_RANGE) {
890: fprintf (stderr,
891: "%s: file '%s' does not have a valid height dimension\n",
892: progname, filename);
893: exit(1);
894: }
895: } /* end of DimensionsFromFile procedure */
896:
897:
898: InitialValuesFromFile(file)
899: FILE *file;
900: {
901: register int i, status;
902: char s[81];
903:
904: status = fscanf (file, "static short %80s = { 0x%4hx", s, &raster[0]);
905: if ((status != 2) || !StringEndsWith (s, "bits[]")) {
906: fprintf (stderr,
907: "%s: file '%s' has an invalid 1st array element\n",
908: progname, filename);
909: exit (1);
910: }
911: for (i=1;i<raster_length;i++) {
912: status = fscanf (file, ", 0x%4hx", &raster[i]);
913: if (status != 1) {
914: fprintf (stderr,
915: "%s: file '%s' has an invalid %dth array element\n",
916: progname, filename, i+1);
917: exit (1);
918: }
919: }
920:
921: } /* end of InitialValuesFromFile procedure */
922:
923:
924: char *StripName(name)
925: char *name;
926: {
927: char *rindex(), *index();
928: char *begin = rindex (name, '/');
929: char *end, *result;
930: int length;
931: begin = (begin ? begin+1 : name);
932: end = index (begin, '.');
933: length = (end ? (end - begin) : strlen (begin));
934: result = (char *) malloc (length + 1);
935: strncpy (result, begin, length);
936: result [length] = '\0';
937: return (result);
938: }
939:
940:
941: char *BackupName(name)
942: char *name;
943: {
944: int name_length = strlen (name);
945: char *result = (char *) malloc (name_length+2);
946: strncpy (result, name, name_length);
947: result [name_length] = '~';
948: result [name_length+1] = '\0';
949: return (result);
950: }
951:
952: char *TmpFileName(name)
953: char *name;
954: {
955: {
956: char *rindex();
957: char *begin = rindex (name, '/');
958: if (begin)
959: name = begin+1;
960: }
961: {
962: char *tmp = "/tmp/";
963: int name_length = strlen (name);
964: int tmp_length = strlen (tmp);
965: int result_length = name_length + tmp_length;
966: char *result = (char *) malloc (result_length + 1);
967: strncpy (result, tmp, tmp_length);
968: strncpy (result+tmp_length, name, name_length);
969: result [result_length] = '\0';
970: return (result);
971: }
972: }
973:
974: /* StringEndsWith returns TRUE if "s" ends with "suffix", else returns FALSE */
975: boolean StringEndsWith (s, suffix)
976: char *s, *suffix;
977: {
978: int s_len = strlen (s);
979: int suffix_len = strlen (suffix);
980: return (strcmp (s + s_len - suffix_len, suffix) == 0);
981: }
982:
983: /* LayoutStage1 determines the size and y-position of all commmand
984: windows and both raster windows. It fills in everything in the
985: commands[] array except the "window" field, and fills in everything in
986: the frames[] array except for the "self" (window) and "x" fields. It
987: returns in *width and *bottom the dimensions of the command area to be
988: created.
989: This routine is called only once, at startup time.
990: Everything done at this stage stays the same even if the user later
991: reshapes the window. */
992:
993: LayoutStage1 ()
994: {
995: int widths [N_COMMANDS];
996: int maxwidth = 0;
997: int ypos = TOP_MARGIN;
998: register int i;
999: register OpaqueFrame *frame;
1000: int ClearOrSetAll(), InvertAll(),
1001: ClearOrSetArea(), InvertArea(), CopyOrMoveArea(),
1002: Line(), Circle(), FilledCircle(),
1003: SetHotSpot(), ClearHotSpot(),
1004: WriteOutput(), Quit();
1005:
1006: commands[0].name = "Clear All";
1007: commands[0].proc = ClearOrSetAll;
1008: commands[0].data = 0;
1009:
1010: commands[1].name = "Set All";
1011: commands[1].proc = ClearOrSetAll;
1012: commands[1].data = 1;
1013:
1014: commands[2].name = "Invert All";
1015: commands[2].proc = InvertAll;
1016:
1017: commands[3].name = "Clear Area";
1018: commands[3].proc = ClearOrSetArea;
1019: commands[3].data = 0;
1020:
1021: commands[4].name = "Set Area";
1022: commands[4].proc = ClearOrSetArea;
1023: commands[4].data = 1;
1024:
1025: commands[5].name = "Invert Area";
1026: commands[5].proc = InvertArea;
1027:
1028: commands[6].name = "Copy Area";
1029: commands[6].proc = CopyOrMoveArea;
1030: commands[6].data = COPY;
1031:
1032: commands[7].name = "Move Area";
1033: commands[7].proc = CopyOrMoveArea;
1034: commands[7].data = MOVE;
1035:
1036: commands[8].name = "Overlay Area";
1037: commands[8].proc = CopyOrMoveArea;
1038: commands[8].data = OVERLAY;
1039:
1040: commands[9].name = "Line";
1041: commands[9].proc = Line;
1042:
1043: commands[10].name = "Circle";
1044: commands[10].proc = Circle;
1045: commands[10].data = 0;
1046:
1047: commands[11].name = "Filled Circle";
1048: commands[11].proc = Circle;
1049: commands[11].data = 1;
1050:
1051: commands[12].name = "Set HotSpot";
1052: commands[12].proc = SetHotSpot;
1053:
1054: commands[13].name = "Clear HotSpot";
1055: commands[13].proc = ClearHotSpot;
1056:
1057: commands[14].name = "Write Output";
1058: commands[14].proc = WriteOutput;
1059:
1060: commands[15].name = "Quit";
1061: commands[15].proc = Quit;
1062:
1063:
1064: for (i=0;i<N_COMMANDS;i++) {
1065: widths[i] = XQueryWidth (commands[i].name, font);
1066: if (maxwidth < widths[i])
1067: maxwidth = widths[i];
1068: }
1069:
1070: maxwidth += 4; /* so even widest command has a little space around it */
1071:
1072: for (i=0;i<N_COMMANDS;i++) {
1073: register struct command_data *command = &commands[i];
1074: frame = &frames[i];
1075: command->name_length = strlen (command->name);
1076: command->x_offset = (maxwidth - widths[i])/2;
1077: frame->y = ypos;
1078: frame->width = maxwidth;
1079: frame->height = fontInfo.height;
1080: frame->bdrwidth = 1;
1081: frame->border = border;
1082: frame->background = backmap;
1083: ypos += fontInfo.height + 5;
1084: if (i==2 || i == 5 || i == 8 || i == 11 || i == 13)
1085: ypos += fontInfo.height + 5;
1086: /* for gaps between groups; pretty random! */
1087:
1088: }
1089:
1090: /* set up raster window */
1091: frame = &frame[N_COMMANDS];
1092: frame = &frames[i];
1093: frame->y = (ypos += AROUND_RASTER_MARGIN);
1094: frame->width = squares_wide + 6;
1095: frame->height = squares_high + 6;
1096: frame->bdrwidth = 1;
1097: frame->border = border;
1098: frame->background = backmap;
1099:
1100: /* raster invert window is the same, except for y position */
1101: *(frame+1) = *frame;
1102: (++frame)->y += squares_high + 8 + AROUND_RASTER_MARGIN;
1103:
1104: }
1105:
1106: /* LayoutStage2 is called both at startup time and whenever the user
1107: resizes the outer window. It figures out what the new grid square size
1108: should be, determines the size and position of all subwindows, then
1109: creates (but does not map) the subwindows. */
1110:
1111: LayoutStage2 ()
1112: {
1113: int x_room = outer_frame.width - 1
1114: - LEFT_MARGIN - frames[0].width - GRID_TO_COMMAND_MARGIN - RIGHT_MARGIN;
1115: int y_room = outer_frame.height - 1
1116: - TOP_MARGIN - BOTTOM_MARGIN;
1117: int i;
1118: int command_x_offset;
1119: OpaqueFrame *grid_frame = &frames[N_COMMANDS+2];
1120:
1121: x_room /= squares_wide;
1122: y_room /= squares_high;
1123: square_size = min (x_room, y_room);
1124:
1125: /* fill in the grid window's frame */
1126: grid_frame->x = LEFT_MARGIN;
1127: grid_frame->y = TOP_MARGIN;
1128: grid_frame->width = (squares_wide * square_size) + 1;
1129: grid_frame->height = (squares_high * square_size) + 1;
1130: grid_frame->bdrwidth = 0;
1131: grid_frame->border = NULL;
1132: grid_frame->background = backmap;
1133:
1134: /* fill in x offsets for command window frames */
1135: command_x_offset = grid_frame->x + grid_frame->width
1136: + GRID_TO_COMMAND_MARGIN;
1137: for (i=0;i<N_COMMANDS;i++)
1138: frames[i].x = command_x_offset;
1139:
1140: /* fill in x offsets for raster and raster-inverted window frames */
1141: frames[N_COMMANDS].x = frames[N_COMMANDS+1].x =
1142: grid_frame->x + grid_frame->width + AROUND_RASTER_MARGIN;
1143:
1144: /* create all the subwindows */
1145: XCreateWindows (outer_window, frames, N_COMMANDS+3);
1146:
1147: /* stow away all the resulting window id's, and select input */
1148: for (i=0;i<N_COMMANDS;i++)
1149: XSelectInput (commands[i].window = frames[i].self,
1150: ButtonPressed | ButtonReleased | LeaveWindow | ExposeWindow);
1151:
1152: XSelectInput (raster_window = frames[N_COMMANDS].self, ExposeWindow);
1153: XSelectInput (raster_invert_window = frames[N_COMMANDS+1].self,
1154: ExposeWindow);
1155: XSelectInput (grid_window = grid_frame->self,
1156: RightDownMotion | MiddleDownMotion | LeftDownMotion
1157: | ExposeRegion | ButtonPressed | ButtonReleased);
1158: /* ButtonReleased is selected for AskUserForArea's benefit */
1159:
1160: }
1161:
1162: /* OuterWindowDims determines the minimum size for the outer window,
1163: based on three constraints: the grid square size, the width of
1164: the command/raster area, and the minimum height of the
1165: command/raster area ("right side" of the window). It is called
1166: at startup time. */
1167:
1168: OuterWindowDims (square_size, right_side_width,
1169: right_side_bottom, width, height)
1170: int square_size, right_side_width, right_side_bottom;
1171: int *width, *height; /* RETURN */
1172: {
1173: *width = LEFT_MARGIN + squares_wide*square_size + 1 + right_side_width;
1174: *height = TOP_MARGIN + squares_high*square_size + 1 + BOTTOM_MARGIN;
1175: if (*height < right_side_bottom)
1176: *height = right_side_bottom;
1177: }
1178:
1179:
1180: ClearOrSetAll(b)
1181: bit b; /* 0 for clear, 1 for set */
1182: {
1183: register int i;
1184: register int new = (b ? ~0: 0);
1185: for (i=0;i<raster_length;i++)
1186: raster[i] = new;
1187: changed = TRUE;
1188: XPixSet (
1189: grid_window, /* window */
1190: 0, /* x */
1191: 0, /* y */
1192: squares_wide*square_size+1, /* width */
1193: squares_high*square_size+1, /* height */
1194: b ? foreground : background);
1195: RepaintGridLines (b ? e_AgainstForeground : e_AgainstBackground);
1196: RepaintRaster();
1197: RepaintRasterInverted();
1198: if (x_hot_spot != OUT_OF_RANGE)
1199: HighlightHotSpot();
1200: } /* end of ClearAll procedure */
1201:
1202:
1203: ClearOrSetArea(b)
1204: bit b; /* 0 for clear, 1 for set */
1205: {
1206: int x1, y1, x2, y2;
1207: register int x, y;
1208: if (AskUserForArea (&x1, &y1, &x2, &y2))
1209: return;
1210: for (x=x1;x<=x2;x++)
1211: for (y=y1;y<=y2;y++)
1212: SetRasterBit (raster, x, y, b);
1213: XPixSet (
1214: grid_window, /* window */
1215: x1*square_size+1, /* x */
1216: y1*square_size+1, /* y */
1217: (x2-x1+1)*square_size-1, /* width */
1218: (y2-y1+1)*square_size-1, /* height */
1219: b ? foreground : background);
1220: RepaintGridLinesPartially (x1, y1, x2+1, y2+1, b ? e_AgainstForeground : e_AgainstBackground, FALSE);
1221: if (x_hot_spot >= x1 && x_hot_spot <= x2
1222: && y_hot_spot >= y1 && y_hot_spot <= y2)
1223: HighlightHotSpot();
1224: changed = TRUE;
1225: RepaintRaster();
1226: RepaintRasterInverted();
1227: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1228: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1229: } /* end of ClearArea procedure */
1230:
1231:
1232: InvertAll() {
1233: register int i;
1234: for (i=0;i<raster_length;i++)
1235: raster[i] ^= ~0; /* invert = exclusive or with all 1's */
1236: changed = TRUE;
1237: XPixFill (
1238: grid_window, /* window */
1239: 0, /* x */
1240: 0, /* y */
1241: squares_wide*square_size+1, /* width */
1242: squares_high*square_size+1, /* height */
1243: 1, /* pixel */
1244: NULL, /* clipmask */
1245: GXinvert, /* function */
1246: invertplane); /* plane mask */
1247: RepaintGridLines (e_Invert);
1248: RepaintRaster();
1249: RepaintRasterInverted();
1250: } /* end of InvertAll procedure */
1251:
1252:
1253: InvertArea() {
1254: int x1, y1, x2, y2;
1255: register int x, y;
1256: if (AskUserForArea (&x1, &y1, &x2, &y2))
1257: return;
1258: for (x=x1;x<=x2;x++)
1259: for (y=y1;y<=y2;y++)
1260: InvertRasterBit (raster, x, y);
1261: ExThroughRectangle (x1, y1, x2, y2); /* wipe out X-outs */
1262: XPixFill (
1263: grid_window, /* window */
1264: x1*square_size+1, /* x */
1265: y1*square_size+1, /* y */
1266: (x2-x1+1)*square_size-1, /* width */
1267: (y2-y1+1)*square_size-1, /* height */
1268: 1, /* pixel */
1269: NULL, /* clipmask */
1270: GXinvert, /* function */
1271: invertplane); /* plane mask */
1272: RepaintGridLinesPartially (x1, y1, x2+1, y2+1, e_Invert, FALSE);
1273: changed = TRUE;
1274: RepaintRaster();
1275: RepaintRasterInverted();
1276: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1277: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1278: } /* end of InvertArea procedure */
1279:
1280:
1281: CopyOrMoveArea (what)
1282: {
1283: int x1, y1, x2, y2;
1284: int x1dest, y1dest;
1285: if (AskUserForArea (&x1, &y1, &x2, &y2))
1286: return;
1287: if (AskUserForDest (&x1dest, &y1dest, x2-x1+1, y2-y1+1))
1288: /* button released outside grid */
1289: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through,
1290: x2_square_exed_through, y2_square_exed_through);
1291: else {
1292: register int xsrc, ysrc, xdest, ydest;
1293: register short *new_raster =
1294: (short *) malloc (raster_length*sizeof(short));
1295:
1296: if (x_hot_spot != OUT_OF_RANGE)
1297: HighlightHotSpot(); /* actually UNhighlight it */
1298:
1299: /* copy whole raster to new raster */
1300: bcopy (raster, new_raster, raster_length*sizeof(short));
1301:
1302: if (what == MOVE)
1303: /* clear source bits in new raster. this is VERY inefficient.
1304: sure wish we had BitBlt available in user memory! */
1305: for (xsrc = x1; xsrc <= x2; xsrc++)
1306: for (ysrc = y1; ysrc <= y2; ysrc++)
1307: SetRasterBit (new_raster, xsrc, ysrc, 0);
1308:
1309: /* copy old source bits to new destination. this is VERY inefficient.
1310: sure wish we had BitBlt available in user memory! */
1311:
1312: for (xsrc = x1, xdest = x1dest;
1313: xsrc<=x2 && xdest < squares_wide; xsrc++, xdest++)
1314: for (ysrc = y1, ydest = y1dest;
1315: ysrc<=y2 && ydest < squares_high; ysrc++, ydest++)
1316: if (what == OVERLAY) {
1317: if (GetRasterBit (raster, xsrc, ysrc))
1318: SetRasterBit (new_raster, xdest, ydest, 1);
1319: } else
1320: SetRasterBit (new_raster, xdest, ydest,
1321: GetRasterBit (raster, xsrc, ysrc));
1322:
1323: free (raster);
1324: raster = new_raster;
1325: if (what == MOVE)
1326: RepaintRectangles (x1, y1, x2, y2, x1dest, y1dest);
1327: else {
1328: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through,
1329: x2_square_exed_through, y2_square_exed_through);
1330: RefillGridPartially (x1dest, y1dest, xdest-1, ydest-1, TRUE);
1331: }
1332:
1333: if (x_hot_spot != OUT_OF_RANGE)
1334: HighlightHotSpot(); /* put the hot spot back on the screen */
1335:
1336: RepaintRaster();
1337: RepaintRasterInverted();
1338: changed = TRUE;
1339: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE;
1340: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE;
1341: }
1342:
1343: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1344: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1345: } /* end of CopyArea procedure */
1346:
1347: #define MAX(a,b) (((a) > (b)) ? (a) : (b))
1348: #define MIN(a,b) (((a) < (b)) ? (a) : (b))
1349: #define ABS(a) (((a) >= 0) ? (a) : -(a))
1350: #define CheckSetRasterBit(r,x,y,c) \
1351: if ((x) >= 0 && (x) < squares_wide && (y) >= 0 && (y) < squares_high) \
1352: SetRasterBit(r, x, y, c)
1353:
1354: Line ()
1355: {
1356: int i, x1, y1, x2, y2;
1357: double dx, dy, x, y, diff;
1358:
1359: if (AskUserForPoint(&x1, &y1, 0))
1360: return;
1361: if (AskUserForPoint(&x2, &y2, 1))
1362: return;
1363: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through,
1364: x2_square_exed_through, y2_square_exed_through);
1365: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
1366: x2_square_plus_through, y2_square_plus_through);
1367:
1368: dx = x2 - x1;
1369: dy = y2 - y1;
1370: x = x1 + 0.5;
1371: y = y1 + 0.5;
1372: diff = MAX(ABS(dx), ABS(dy));
1373: if (diff == 0)
1374: diff = 0.9;
1375: dx /= diff;
1376: dy /= diff;
1377: for (i = 0; i <= (int)diff; i++) {
1378: SetRasterBit(raster, (int)x, (int)y, 1);
1379: x += dx;
1380: y += dy;
1381: }
1382: RefillGridPartially(MIN(x1, x2), MIN(y1, y2), MAX(x1, x2), MAX(y1, y2),
1383: FALSE);
1384: changed = TRUE;
1385: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1386: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1387: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE;
1388: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE;
1389: RepaintRaster();
1390: RepaintRasterInverted();
1391: }
1392:
1393: #include <math.h>
1394:
1395: Circle(filled)
1396: {
1397: int i, j, x, x1, y1, x2, y2, dx, dy;
1398: double rad, half;
1399:
1400: if (AskUserForPoint(&x1, &y1, 0))
1401: return;
1402: if (AskUserForPoint(&x2, &y2, 1))
1403: return;
1404: ExThroughRectangle (x1_square_exed_through, y1_square_exed_through,
1405: x2_square_exed_through, y2_square_exed_through);
1406: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
1407: x2_square_plus_through, y2_square_plus_through);
1408:
1409: dx = x2 - x1;
1410: dy = y2 - y1;
1411: rad = sqrt((double)(dx * dx + dy * dy)) + 0.5;
1412: if (filled)
1413: for (i = 0; i <= (int)rad; i++) {
1414: x = sqrt(rad * rad - i * i);
1415: for (j = x1 - x; j <= x1 + x; j++) {
1416: CheckSetRasterBit(raster, j, y1 - i, 1);
1417: CheckSetRasterBit(raster, j, y1 + i, 1);
1418: }
1419: }
1420: else {
1421: half = rad * sqrt(2.0)/2;
1422: for (i = 0; i <= (int)half; i++) {
1423: x = sqrt(rad * rad - i * i);
1424: CheckSetRasterBit(raster, x1 - x, y1 - i, 1);
1425: CheckSetRasterBit(raster, x1 - x, y1 + i, 1);
1426: CheckSetRasterBit(raster, x1 + x, y1 - i, 1);
1427: CheckSetRasterBit(raster, x1 + x, y1 + i, 1);
1428: CheckSetRasterBit(raster, x1 - i, y1 - x, 1);
1429: CheckSetRasterBit(raster, x1 - i, y1 + x, 1);
1430: CheckSetRasterBit(raster, x1 + i, y1 - x, 1);
1431: CheckSetRasterBit(raster, x1 + i, y1 + x, 1);
1432: }
1433: }
1434: RefillGridPartially(x1-(int)rad, y1-(int)rad,
1435: x1+(int)rad, y1+(int)rad, FALSE);
1436: changed = TRUE;
1437: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1438: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1439: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE;
1440: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE;
1441: RepaintRaster();
1442: RepaintRasterInverted();
1443: }
1444:
1445: ClearHotSpot() {
1446: if (x_hot_spot == OUT_OF_RANGE)
1447: return;
1448: HighlightHotSpot(); /* UNhighlight existing hot spot */
1449: x_hot_spot = y_hot_spot = OUT_OF_RANGE;
1450: changed = TRUE;
1451: }
1452:
1453: SetHotSpot() {
1454: XCompressEvents();
1455: XDefineCursor (outer_window, dot);
1456: XSelectInput (outer_window, ButtonPressed | ButtonReleased | ExposeWindow);
1457: /* so that we can detect button pressed outside grid */
1458:
1459: while (TRUE) {
1460: XEvent event;
1461: int x1, y1;
1462: XNextEvent (&event);
1463: switch (event.type) {
1464:
1465: case ButtonPressed:
1466: case MouseMoved:
1467: if ((event.window == grid_window)
1468: && !WhatSquare (&event, &x1, &y1)
1469: && (x_hot_spot != x1 || y_hot_spot != y1)) {
1470:
1471: /* UNhighlight old hot spot */
1472: if (x_hot_spot != OUT_OF_RANGE)
1473: HighlightHotSpot();
1474:
1475: x_hot_spot = x1;
1476: y_hot_spot = y1;
1477:
1478: /* highlight new hot spot */
1479: HighlightHotSpot();
1480:
1481: changed = TRUE;
1482: }
1483: break; /* keep looping until button is released */
1484:
1485: case ButtonReleased:
1486: XExpandEvents();
1487: XDefineCursor (outer_window, cross);
1488: XSelectInput (outer_window, ExposeWindow);
1489: return;
1490:
1491: case ExposeWindow:
1492: case ExposeRegion:
1493: ProcessEvent (&event);
1494: break;
1495:
1496: default:
1497: break; /* just throw it away */
1498:
1499: }
1500: }
1501: }
1502:
1503: RepaintRectangles (x1, y1, x2, y2, x3, y3)
1504: int x1, y1; /* first rectangle's top & left */
1505: int x2, y2; /* first rectangle's bottom & right */
1506: int x3, y3; /* second rectangle's top & left */
1507: {
1508: int x4 = x3 + (x2 - x1); /* second rectangle's right edge */
1509: int y4 = y3 + (y2 - y1); /* second rectangle's bottom edge */
1510:
1511: if (x4 >= squares_wide) x4 = squares_wide-1;
1512: if (y4 >= squares_wide) y4 = squares_high-1;
1513:
1514: /* if first rectangle is right of second, swap "first" and "second" variables */
1515: if (x1 > x3)
1516: {int temp;
1517: #define swap(a,b) {temp = a; a = b; b = temp;}
1518: swap (x1, x3); swap (y1, y3); swap (x2, x4); swap (y2, y4);
1519: #undef swap
1520: }
1521:
1522: RefillGridPartially (x1, y1, x2, y2, TRUE);
1523:
1524: if ((x3 > x2) || (max (y1, y3) > min (y2, y4)))
1525: /* rectangles don't overlap */
1526: RefillGridPartially (x3, y3, x4, y4, TRUE);
1527:
1528: else if (y1 < y3) {
1529: /* second rectangle is below & right of first */
1530: RefillGridPartially (x2+1, y3, x4, y2, TRUE);
1531: RefillGridPartially (x3, y2+1, x4, y4, TRUE);
1532: }
1533:
1534: else {
1535: /* second rectangle is above & right of first */
1536: RefillGridPartially (x3, y3, x4, y1-1, TRUE);
1537: RefillGridPartially (x2+1, y1, x4, y4, TRUE);
1538: }
1539: }
1540:
1541:
1542: /* AskUserForArea returns FALSE if the user has defined a valid area,
1543: TRUE if the user hasn't (e.g. by clicking outside grid) */
1544:
1545: boolean AskUserForArea(px1, py1, px2, py2)
1546: int *px1, *py1, *px2, *py2;
1547: {
1548: XEvent event;
1549: int x1, y1, x2, y2;
1550: boolean result;
1551:
1552: XSelectInput (outer_window, ButtonPressed | ExposeWindow);
1553: /* so that we can detect button pressed outside grid */
1554:
1555: XDefineCursor (outer_window, upper_left);
1556:
1557: while (TRUE) {
1558: XNextEvent (&event);
1559: switch (event.type) {
1560: case ButtonPressed:
1561: if ((event.window != grid_window)
1562: || WhatSquare (&event, &x1, &y1)) {
1563: XDefineCursor (outer_window, cross);
1564: XSelectInput (outer_window, ExposeWindow);
1565: return (TRUE);
1566: }
1567: goto out1; /* get out of the loop */
1568: case ExposeWindow:
1569: case ExposeRegion:
1570: ProcessEvent (&event);
1571: break;
1572: default:
1573: break; /* just throw it away */
1574: }
1575: }
1576:
1577: out1:
1578: XCompressEvents(); /* DO collapse consecutive MouseMoved events */
1579: ExThroughSquare (x1, y1);
1580: FlushLineBuffer();
1581: x1_square_exed_through = x2_square_exed_through = x2 = x1;
1582: y1_square_exed_through = y2_square_exed_through = y2 = y1;
1583: XDefineCursor (outer_window, lower_right);
1584:
1585: while (TRUE) {
1586: XNextEvent (&event);
1587: switch (event.type) {
1588: case ButtonPressed:
1589: result = TRUE;
1590: goto out2;
1591: case ExposeWindow:
1592: case ExposeRegion:
1593: ProcessEvent (&event);
1594: break;
1595: case MouseMoved:
1596: case ButtonReleased: {
1597: int x, y;
1598: result = (event.window != grid_window)
1599: || WhatSquare (&event, &x, &y) /* mouse outside grid? */
1600: || (x < x1) || (y < y1);
1601: if (result) {
1602: ExThroughRectangle (x1+1, y1, x2, y2);
1603: ExThroughRectangle (x1, y1+1, x1, y2);
1604: x2 = x2_square_exed_through = x1;
1605: y2 = y2_square_exed_through = y1;
1606: }
1607: else if ((x == x2) && (y == y2))
1608: ; /* both dimensions the same; do nothing */
1609: else if ((x > x2) == (y > y2)) {
1610: /* both dimensions bigger or smaller */
1611: ExThroughRectangle (min(x2,x)+1, y1, max(x2,x), max(y2,y));
1612: ExThroughRectangle (x1, min(y2,y)+1, min(x2,x), max(y2,y));
1613: x2 = x2_square_exed_through = x;
1614: y2 = y2_square_exed_through = y;
1615: }
1616: else {
1617: /* one dimension bigger, the other smaller */
1618: ExThroughRectangle (min(x2,x)+1, y1, max(x2,x), min(y2,y));
1619: ExThroughRectangle (x1, min(y2,y)+1, min(x2,x), max(y2,y));
1620: x2 = x2_square_exed_through = x;
1621: y2 = y2_square_exed_through = y;
1622: }
1623: if (event.type == ButtonReleased)
1624: goto out2;
1625: break;
1626: }
1627: default:
1628: break; /* just throw it away */
1629: }
1630: }
1631:
1632: out2:
1633: XSelectInput (outer_window, ExposeWindow);
1634: XDefineCursor (outer_window, cross);
1635: if (result) {
1636: /* no area properly selected; remove X-outs from display */
1637: ExThroughRectangle (x1, y1, x2, y2);
1638: x1_square_exed_through = y1_square_exed_through = OUT_OF_RANGE;
1639: x2_square_exed_through = y2_square_exed_through = OUT_OF_RANGE;
1640: }
1641: else {
1642: *px1 = x1;
1643: *px2 = x2;
1644: *py1 = y1;
1645: *py2 = y2;
1646: }
1647: XExpandEvents();
1648: return (result);
1649: } /* end of AskUserForArea procedure */
1650:
1651: boolean AskUserForDest (px1, py1, width, height)
1652: int *px1, *py1;
1653: int width, height;
1654: {
1655: XEvent event;
1656: boolean result;
1657: XCompressEvents(); /* DO collapse consecutive MouseMoved events */
1658: XSelectInput (outer_window, ButtonPressed | ButtonReleased | ExposeWindow);
1659: /* so we can detect button action outside grid */
1660: XDefineCursor (outer_window, upper_left);
1661:
1662: while (TRUE) {
1663: XNextEvent (&event);
1664: switch (event.type) {
1665:
1666: case ExposeWindow:
1667: case ExposeRegion:
1668: ProcessEvent (&event);
1669: break;
1670:
1671: case ButtonPressed:
1672: case MouseMoved: {
1673: int x1_new, y1_new;
1674: boolean this_window = (event.window == grid_window)
1675: && !WhatSquare (&event, &x1_new, &y1_new);
1676:
1677: if (this_window && (x1_new == *px1) && (y1_new == *py1))
1678: break; /* mouse is still in same square as before; do nothing */
1679:
1680: if (x1_square_plus_through != OUT_OF_RANGE)
1681: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
1682: x2_square_plus_through, y2_square_plus_through);
1683:
1684: if (this_window) {
1685: *px1 = x1_square_plus_through = x1_new;
1686: *py1 = y1_square_plus_through = y1_new;
1687: x2_square_plus_through = min (x1_new + width, squares_wide) - 1;
1688: y2_square_plus_through = min (y1_new + height, squares_high) - 1;
1689: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
1690: x2_square_plus_through, y2_square_plus_through);
1691: }
1692: else {
1693: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE;
1694: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE;
1695: *px1 = *py1 = OUT_OF_RANGE;
1696: }
1697: break;
1698: }
1699:
1700: case ButtonReleased: {
1701: result = (event.window != grid_window)
1702: || WhatSquare (&event, px1, py1);
1703: goto out;
1704: }
1705:
1706: default:
1707: break; /* throw it away */
1708: }
1709: }
1710:
1711: out:
1712: if (result) {
1713: /* button released outside grid */
1714: if (x1_square_plus_through != OUT_OF_RANGE)
1715: PlusThroughRectangle (x1_square_plus_through, y1_square_plus_through,
1716: x2_square_plus_through, y2_square_plus_through);
1717: x1_square_plus_through = y1_square_plus_through = OUT_OF_RANGE;
1718: x2_square_plus_through = y2_square_plus_through = OUT_OF_RANGE;
1719: }
1720:
1721: XExpandEvents();
1722: XSelectInput (outer_window, ExposeWindow);
1723: XDefineCursor (outer_window, cross);
1724: return (result);
1725: } /* end of AskUserForDest procedure */
1726:
1727: boolean AskUserForPoint (xp, yp, plus)
1728: int *xp, *yp;
1729: {
1730: XEvent event;
1731: boolean this_window;
1732:
1733: XCompressEvents(); /* DO collapse consecutive MouseMoved events */
1734: XSelectInput (outer_window, ButtonPressed | ExposeWindow);
1735: /* so we can detect button action outside grid */
1736: XDefineCursor (outer_window, dot);
1737:
1738: while (TRUE) {
1739: XNextEvent (&event);
1740: switch (event.type) {
1741:
1742: case ExposeWindow:
1743: case ExposeRegion:
1744: ProcessEvent (&event);
1745: break;
1746:
1747: case ButtonReleased:
1748: this_window = (event.window == grid_window)
1749: && !WhatSquare (&event, xp, yp);
1750: if (this_window) {
1751: if (plus) {
1752: PlusThroughRectangle (*xp, *yp, *xp, *yp);
1753: x1_square_plus_through = x2_square_plus_through = *xp;
1754: y1_square_plus_through = y2_square_plus_through = *yp;
1755: } else {
1756: ExThroughRectangle (*xp, *yp, *xp, *yp);
1757: x1_square_exed_through = x2_square_exed_through = *xp;
1758: y1_square_exed_through = y2_square_exed_through = *yp;
1759: }
1760: }
1761: goto out;
1762: break;
1763:
1764: default:
1765: break; /* throw it away */
1766: }
1767: }
1768:
1769: out:
1770:
1771: XExpandEvents();
1772: XSelectInput (outer_window, ExposeWindow);
1773: XDefineCursor (outer_window, cross);
1774: return (!this_window);
1775: }
1776:
1777: DialogInputHandler (event)
1778: XEvent *event;
1779: {
1780: switch (event->type) {
1781: case ExposeWindow:
1782: case ExposeRegion:
1783: ProcessEvent (event);
1784: }
1785: }
1786:
1787: enum output_error {e_rename, e_write};
1788:
1789: /* WriteOutput returns TRUE if output successfully written, FALSE if not */
1790:
1791: WriteOutput() {
1792: FILE *file;
1793: if (!changed)
1794: return (TRUE);
1795: if (rename (filename, backup_filename) && errno != ENOENT)
1796: return (HandleOutputError(e_rename));
1797: file = fopen (filename, "w+");
1798: if (!file)
1799: return (HandleOutputError(e_write));
1800: WriteOutputToFile (file);
1801: fclose (file);
1802: changed = FALSE;
1803: return (TRUE);
1804: }
1805:
1806:
1807: /* HandleOutputError returns TRUE if alternate file written, FALSE if not */
1808:
1809: int HandleOutputError(e)
1810: enum output_error e;
1811: {
1812: int result;
1813: char *strings[2];
1814: char msg1[120], msg2[120];
1815: char *tmp_filename;
1816: if (e == e_rename)
1817: sprintf (msg1, "Can't rename %s to %s -- %s",
1818: filename, backup_filename, sys_errlist[errno]);
1819: else
1820: sprintf (msg1, "Can't write on file %s -- %s",
1821: filename, sys_errlist[errno]);
1822: tmp_filename = TmpFileName (filename);
1823: sprintf (msg2, "Should I write output to file %s?", tmp_filename);
1824: strings[0] = "Yes";
1825: strings[1] = "No";
1826: result = dialog (outer_window, font, fontInfo.height,
1827: msg1, msg2, strings, 2, DialogInputHandler);
1828:
1829: if (result == 0) /* "yes" */ {
1830: filename = tmp_filename;
1831: free (backup_filename);
1832: backup_filename = BackupName (filename);
1833: return (WriteOutput());
1834: }
1835: else { /* "no" */
1836: free (tmp_filename);
1837: return (FALSE);
1838: }
1839: }
1840:
1841:
1842: Quit() {
1843: if (changed) {
1844: int result;
1845: char *strings[3];
1846: strings[0] = "Yes";
1847: strings[1] = "No";
1848: strings[2] = "Cancel";
1849: result = dialog (outer_window, font, fontInfo.height,
1850: "Save changes before quitting?", "", strings, 3, DialogInputHandler);
1851:
1852: switch (result) {
1853: case 0: /* "yes" */
1854: if (WriteOutput())
1855: exit(0);
1856: else return;
1857: case 1: /* "no" */
1858: exit(0);
1859: default: /* "cancel" */
1860: return;
1861: }
1862: }
1863:
1864: exit(0);
1865: }
1866:
1867: HighlightHotSpot() {
1868: /* Draw a diamond in the hot spot square */
1869: /* x1 and y1 are the center of the hot spot square */
1870: register int x1 = x_hot_spot*square_size + square_size/2;
1871: register int y1 = y_hot_spot*square_size + square_size/2;
1872: register int radius = square_size/6;
1873: register int i;
1874: Vertex v[5];
1875: v[0].x = v[2].x = v[4].x = x1;
1876: v[1].x = x1 + radius;
1877: v[3].x = x1 - radius;
1878: v[0].y = v[4].y = y1 + radius;
1879: v[1].y = v[3].y = y1;
1880: v[2].y = y1 - radius;
1881: for (i=0;i<5;i++)
1882: v[i].flags = 0;
1883: XDraw (grid_window, v, 5, 1, 1, 1, GXinvert, highlightplane);
1884: }
1885:
1886: ExThroughRectangle (x1, y1, x2, y2)
1887: register int x1, y1, x2, y2;
1888: {
1889: register int x, y;
1890: for (x=x1;x<=x2;x++)
1891: for (y=y1;y<=y2;y++)
1892: ExThroughSquare (x, y);
1893: FlushLineBuffer();
1894: }
1895:
1896:
1897: ExThroughSquare (x, y)
1898: register int x, y;
1899: {
1900: register int x1 = x*square_size;
1901: register int y1 = y*square_size;
1902: LineIntoBuffer (x1+1, y1+1,
1903: x1+square_size, y1+square_size);
1904: LineIntoBuffer (x1+square_size-1, y1+1,
1905: x1, y1+square_size);
1906: }
1907:
1908:
1909: PlusThroughRectangle (x1, y1, x2, y2)
1910: register int x1, y1, x2, y2;
1911: {
1912: register int x, y;
1913: for (x=x1;x<=x2;x++)
1914: for (y=y1;y<=y2;y++)
1915: PlusThroughSquare (x, y);
1916: FlushLineBuffer();
1917: }
1918:
1919: PlusThroughSquare (x, y)
1920: register int x, y;
1921: {
1922: register int x1 = x*square_size;
1923: register int y1 = y*square_size;
1924: LineIntoBuffer (x1+square_size/2, y1+1,
1925: x1+square_size/2, y1+square_size);
1926: LineIntoBuffer (x1+1, y1+square_size/2,
1927: x1+square_size, y1+square_size/2);
1928: }
1929:
1930:
1931: #define BUFFER_MAXLENGTH 200 /* must be even */
1932: static Vertex buffer [BUFFER_MAXLENGTH];
1933: static int buffer_length = 0;
1934:
1935: LineIntoBuffer (x1, y1, x2, y2) {
1936: buffer [buffer_length].x = x1;
1937: buffer [buffer_length].y = y1;
1938: buffer [buffer_length++].flags = VertexDontDraw;
1939: buffer [buffer_length].x = x2;
1940: buffer [buffer_length].y = y2;
1941: buffer [buffer_length++].flags = 0;
1942: if (buffer_length == BUFFER_MAXLENGTH)
1943: FlushLineBuffer();
1944: }
1945:
1946: FlushLineBuffer () {
1947: XDraw (grid_window, buffer, buffer_length, 1, 1, 1, GXinvert,highlightplane);
1948: buffer_length = 0;
1949: }
1950:
1951: #ifdef romp
1952: /*
1953: * prerelease IBM RT/PC software does not have ffs in its C library.
1954: * This code should be thrown away by summer, 1986.
1955: */
1956: int ffs(i)
1957: int i;
1958: {
1959: int j = 1;
1960: if (i == 0) return (0);
1961: while (1) {
1962: if (i & 1) return (j);
1963: j++;
1964: i >>= 1;
1965: }
1966: }
1967:
1968: #endif
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.