Annotation of researchv9/X11/src/X.V11R1/clients/bitmap/bitmap.c, revision 1.1

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

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.