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