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