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