|
|
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]; ! 284: setlinebuf (stderr); ! 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); ! 1636: if (rename (filename, backup_filename) && errno != ENOENT) ! 1637: return (HandleOutputError(e_rename)); ! 1638: file = fopen (filename, "w+"); ! 1639: if (!file) ! 1640: return (HandleOutputError(e_write)); ! 1641: WriteOutputToFile (file); ! 1642: fclose (file); ! 1643: changed = FALSE; ! 1644: return (TRUE); ! 1645: } ! 1646: ! 1647: ! 1648: /* HandleOutputError returns TRUE if alternate file written, FALSE if not */ ! 1649: ! 1650: int HandleOutputError(e) ! 1651: enum output_error e; ! 1652: { ! 1653: int result; ! 1654: static char *strings[] = {"Yes", "No"}; ! 1655: char msg1[120], msg2[120]; ! 1656: char *tmp_filename; ! 1657: if (e == e_rename) ! 1658: sprintf (msg1, "Can't rename %s to %s -- %s", ! 1659: filename, backup_filename, sys_errlist[errno]); ! 1660: else ! 1661: sprintf (msg1, "Can't write on file %s -- %s", ! 1662: filename, sys_errlist[errno]); ! 1663: tmp_filename = TmpFileName (filename); ! 1664: sprintf (msg2, "Should I write output to file %s?", tmp_filename); ! 1665: result = dialog (outer_window, font, ! 1666: msg1, msg2, strings, 2, DialogInputHandler); ! 1667: ! 1668: if (result == 0) /* "yes" */ { ! 1669: filename = tmp_filename; ! 1670: free (backup_filename); ! 1671: backup_filename = BackupName (filename); ! 1672: return (WriteOutput()); ! 1673: } ! 1674: else { /* "no" */ ! 1675: free (tmp_filename); ! 1676: return (FALSE); ! 1677: } ! 1678: } ! 1679: ! 1680: ! 1681: void Quit() { ! 1682: if (changed) { ! 1683: int result; ! 1684: static char *strings[3] = {"Yes", "No", "Cancel"}; ! 1685: result = dialog (outer_window, font, ! 1686: "Save changes before quitting?", "", strings, 3, DialogInputHandler); ! 1687: ! 1688: switch (result) { ! 1689: case 0: /* "yes" */ ! 1690: if (WriteOutput()) ! 1691: exit(0); ! 1692: else return; ! 1693: case 1: /* "no" */ ! 1694: exit(0); ! 1695: default: /* "cancel" */ ! 1696: return; ! 1697: } ! 1698: } ! 1699: ! 1700: exit(0); ! 1701: } ! 1702: ! 1703: HighlightHotSpot() { ! 1704: /* Draw a diamond in the hot spot square */ ! 1705: /* x1 and y1 are the center of the hot spot square */ ! 1706: register int x1 = x_hot_spot*square_size + square_size/2; ! 1707: register int y1 = y_hot_spot*square_size + square_size/2; ! 1708: register int radius = square_size/6; ! 1709: XPoint points[5]; ! 1710: points[0].x = points[2].x = points[4].x = x1; ! 1711: points[1].x = x1 + radius; ! 1712: points[3].x = x1 - radius; ! 1713: points[0].y = points[4].y = y1 + radius; ! 1714: points[1].y = points[3].y = y1; ! 1715: points[2].y = y1 - radius; ! 1716: XSetLineAttributes (d, gc, 0, LineSolid, CapNotLast, JoinMiter); ! 1717: XSetState (d, gc, 1L, 0L, GXinvert, highlightplane); ! 1718: XDrawLines (d, grid_window, gc, points, 5, CoordModeOrigin); ! 1719: } ! 1720: ! 1721: ExThroughRectangle (x1, y1, x2, y2) ! 1722: register int x1, y1, x2, y2; ! 1723: { ! 1724: register int x, y; ! 1725: for (x=x1;x<=x2;x++) ! 1726: for (y=y1;y<=y2;y++) ! 1727: ExThroughSquare (x, y); ! 1728: FlushLineBuffer(); ! 1729: } ! 1730: ! 1731: ! 1732: ExThroughSquare (x, y) ! 1733: register int x, y; ! 1734: { ! 1735: register int x1 = x*square_size; ! 1736: register int y1 = y*square_size; ! 1737: LineIntoBuffer (x1+1, y1+1, ! 1738: x1+square_size, y1+square_size); ! 1739: LineIntoBuffer (x1+square_size-1, y1+1, ! 1740: x1, y1+square_size); ! 1741: } ! 1742: ! 1743: ! 1744: PlusThroughRectangle (x1, y1, x2, y2) ! 1745: register int x1, y1, x2, y2; ! 1746: { ! 1747: register int x, y; ! 1748: for (x=x1;x<=x2;x++) ! 1749: for (y=y1;y<=y2;y++) ! 1750: PlusThroughSquare (x, y); ! 1751: FlushLineBuffer(); ! 1752: } ! 1753: ! 1754: PlusThroughSquare (x, y) ! 1755: register int x, y; ! 1756: { ! 1757: register int x1 = x*square_size; ! 1758: register int y1 = y*square_size; ! 1759: LineIntoBuffer (x1+square_size/2, y1+1, ! 1760: x1+square_size/2, y1+square_size); ! 1761: LineIntoBuffer (x1+1, y1+square_size/2, ! 1762: x1+square_size, y1+square_size/2); ! 1763: } ! 1764: ! 1765: ! 1766: #define BUFFER_MAXLENGTH 100 ! 1767: static XSegment buffer [BUFFER_MAXLENGTH]; ! 1768: static int buffer_length = 0; ! 1769: ! 1770: LineIntoBuffer (x1, y1, x2, y2) { ! 1771: register XSegment *seg = &buffer[buffer_length]; ! 1772: seg->x1 = x1; ! 1773: seg->y1 = y1; ! 1774: seg->x2 = x2; ! 1775: seg->y2 = y2; ! 1776: if (++buffer_length == BUFFER_MAXLENGTH) ! 1777: FlushLineBuffer(); ! 1778: } ! 1779: ! 1780: FlushLineBuffer () { ! 1781: XSetLineAttributes (d, gc, 0, LineSolid, CapNotLast, JoinMiter); ! 1782: XSetState (d, gc, 1L, 0L, GXinvert, highlightplane); ! 1783: XDrawSegments (d, grid_window, gc, buffer, buffer_length); ! 1784: buffer_length = 0; ! 1785: } ! 1786: ! 1787:
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.