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