home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / fontutils-0.6-base.tgz / fontutils-0.6-base.tar / fsf / fontutils / widgets / Bitmap.c next >
C/C++ Source or Header  |  1992-06-13  |  36KB  |  1,070 lines

  1. /* Bitmap.c: implementation of a Bitmap widget as a subclass of Athena's
  2.    Label widget.
  3.  
  4. Copyright (C) 1992 Free Software Foundation, Inc.
  5.  
  6. This program is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 2, or (at your option)
  9. any later version.
  10.  
  11. This program is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with this program; if not, write to the Free Software
  18. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. #include "config.h"
  21.  
  22. #include "xt-common.h"
  23.  
  24. #include "bitmap.h"
  25.  
  26. #include "BitmapP.h"
  27.  
  28. /* Says whether the rectangle we draw to represent the temporary
  29.    selection is visible, i.e., if we've erased it.  */
  30. static boolean selection_showing = false;
  31.  
  32. /* Utility routines.  */
  33. static Pixmap bitmap_to_pixmap (Display *, bitmap_type, unsigned);
  34. static void check_action_parameters (string, BitmapWidget, Cardinal, Cardinal);
  35. static boolean find_bitmap_position (XEvent *, BitmapWidget, int *, int *);
  36. static coordinate_type get_pointer_position (XEvent *);
  37. static unsigned param_value (string, string []);
  38. static void redraw_pixel (BitmapWidget, one_byte, long, int, int);
  39.  
  40. /* Subroutines for handling selections.  */
  41. static bounding_box_type find_selection_bb (BitmapWidget);
  42. static void redraw_selection_bitmap (BitmapWidget, bitmap_type);
  43. static void redraw_selection (BitmapWidget, bitmap_type);
  44. static void show_selection (BitmapWidget);
  45. static void unshow_selection (BitmapWidget);
  46. static void update_selection (BitmapWidget, unsigned, unsigned);
  47.  
  48.  
  49.  
  50. /* Action routines.  */
  51. static action_proc_type
  52.   invert_pixel, fill_selection,
  53.   start_selection, adjust_selection, accept_selection,
  54.   start_paste, move_paste, accept_paste;
  55.                   
  56. /* The mapping of action names to procedures.  */
  57. static XtActionsRec bitmap_actions[]
  58.   = { { "InvertPixel",               invert_pixel },
  59.       { "FillSelection",    fill_selection },
  60.       { "StartSelection",          start_selection },
  61.       { "AdjustSelection",         adjust_selection },
  62.       { "AcceptSelection",         accept_selection },
  63.       { "StartPaste",               start_paste },
  64.       { "MovePaste",               move_paste },
  65.       { "AcceptPaste",        accept_paste },
  66.     };
  67.  
  68.  
  69. /* This structure defines the default values of the resources specific
  70.    to the Bitmap widget.  */
  71. #define OFFSET(field) XtOffset (BitmapWidget, bitmap.field)
  72. static XtResource bitmap_resources[]
  73.   = { IMMEDIATE_RESOURCE (expansion, Expansion, Dimension,
  74.                           BITMAP_DEFAULT_EXPANSION),
  75.       IMMEDIATE_RESOURCE (bits, Bitmap, Pointer, NULL),
  76.       IMMEDIATE_RESOURCE (modified, Modified, Boolean, False),
  77.       IMMEDIATE_RESOURCE (shadow, Bitmap, Widget, NULL),
  78.     };
  79.  
  80.  
  81. /* Routines in the class record.  */
  82. static void bitmap_initialize (Widget, Widget, ArgList, Cardinal *);
  83. static Boolean bitmap_set_values (Widget, Widget, Widget, ArgList, Cardinal *);
  84. static void bitmap_destroy (Widget);
  85.  
  86. /* We can inherit most things in the instantiation of the class record.
  87.    The operations `initialize', `set_values', and `destroy' are chained
  88.    automatically by the toolkit, and need no `XtInherit...' constant
  89.    (Xt manual, p.21).  */
  90. BitmapClassRec bitmapClassRec
  91.   = { /* Core class fields.  */
  92.       { /* superclass           */ (WidgetClass) &labelClassRec,
  93.         /* class_name           */ "Bitmap",
  94.         /* widget_size           */ sizeof (BitmapRec),
  95.         /* class_initialize        */ NULL,
  96.         /* class_part_initialize */ NULL,
  97.         /* class_inited            */ FALSE,
  98.         /* initialize           */ bitmap_initialize,
  99.         /* initialize_hook     */ NULL,
  100.         /* realize         */ XtInheritRealize,
  101.         /* actions         */ bitmap_actions,
  102.         /* num_actions           */ XtNumber (bitmap_actions),
  103.         /* resources           */ bitmap_resources,
  104.         /* num_resources     */ XtNumber (bitmap_resources),
  105.         /* xrm_class           */ NULLQUARK,
  106.         /* compress_motion     */ TRUE,
  107.         /* compress_exposure       */ TRUE,
  108.         /* compress_enterleave     */ TRUE,
  109.         /* visible_interest     */ FALSE,
  110.         /* destroy         */ bitmap_destroy,
  111.         /* resize         */ XtInheritResize,
  112.         /* expose         */ XtInheritExpose,
  113.         /* set_values           */ bitmap_set_values,
  114.         /* set_values_hook     */ NULL,
  115.         /* set_values_almost     */ XtInheritSetValuesAlmost,
  116.         /* get_values_hook     */ NULL,
  117.         /* accept_focus          */ NULL,
  118.         /* version         */ XtVersion,
  119.         /* callback_private        */ NULL,
  120.         /* tm_table         */ NULL,
  121.         /* query_geometry     */ XtInheritQueryGeometry,
  122.         /* display_accelerator     */ XtInheritDisplayAccelerator,
  123.         /* extension         */ NULL
  124.       },
  125.  
  126.       /* Simple class fields.  */
  127.       { /* change_sensitive */ XtInheritChangeSensitive },
  128.  
  129.       /* Label class fields.  */
  130.       { 0 },
  131.  
  132.       /* Bitmap class fields.  */
  133.       { 0 }
  134.     };
  135.  
  136. WidgetClass bitmapWidgetClass = (WidgetClass) &bitmapClassRec;
  137.  
  138.  
  139.  
  140. /* Class routines.  */
  141.  
  142. /* This routine is called at widget creation time by the toolkit, after
  143.    our superclasses have been initialized.  REQUEST is the widget that
  144.    was originally requested by the user; NEW is the widget that has been
  145.    created by our superclasses in response to the requests.
  146.    
  147.    We want to tell our Label superclass that it should deal with a pixmap,
  148.    not with a string.
  149.    
  150.    We do not use the parameters ARGS and N_ARGS.  */
  151.  
  152. static void
  153. bitmap_initialize (Widget request, Widget new, ArgList args, Cardinal *n_args)
  154. {
  155.   XGCValues select_gc_values;
  156.   BitmapWidget old = xmalloc (sizeof (*old));
  157.   BitmapWidget bw = (BitmapWidget) new;
  158.   bitmap_type *bitmap_ptr = (bitmap_type *) bw->bitmap.bits;
  159.   Arg bitmap_args[] = { { XtNbits, (XtArgVal) bitmap_ptr } };
  160.   Cardinal n = XtNumber (bitmap_args);
  161.   Display *d = XtDisplay (bw);
  162.  
  163.   if (bitmap_ptr == NULL)
  164.     XtErrorMsg ("noData", "bitmapCreate", "BitmapError",
  165.                 "No bitmap specified.", NULL, 0);
  166.  
  167.   /* Convert the bitmap to a pixmap.  We have to make sure that we
  168.      really do create the pixmap here, by forcing the old and new
  169.      bitmaps to be different.  */
  170.   *old = *(BitmapWidget) request;
  171.   old->bitmap.bits = NULL;
  172.   (void) bitmap_set_values ((Widget) old, request, new, bitmap_args, &n);
  173.  
  174.   /* Make GC's to use in editing the bits and doing the selection.  */
  175.   bw->bitmap.edit_gc = XCreateGC (XtDisplay (bw), bw->label.pixmap, 0, NULL);
  176.   select_gc_values.function = GXxor;
  177.   select_gc_values.foreground = AllPlanes;
  178.   bw->bitmap.select_gc = XCreateGC (d, bw->label.pixmap,
  179.                                     GCFunction | GCForeground,
  180.                                     &select_gc_values);
  181.  
  182.   /* If we have a shadow bitmap, we want to use the same GC and bitmap
  183.      for it as we do for the current one.  We should probably do this in
  184.      bitmap_set_values somewhere, also.  */
  185.   if (bw->bitmap.shadow != NULL)
  186.     {
  187.       BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
  188.       XFreeGC (d, shadow_bw->bitmap.edit_gc);
  189.       shadow_bw->bitmap.edit_gc = bw->bitmap.edit_gc;
  190.     }
  191.  
  192.   /* Initially, we're unmodified, and have no selection.  */
  193.   bw->bitmap.modified = false;
  194.   bw->bitmap.selection = NULL;
  195. }
  196.  
  197.  
  198. /* This routine is called when one of the resources in the widget
  199.    changes; for example, in response to an XtSetValues call.  It is also
  200.    called by the `bitmap_initialize' routine.  The toolkit has already
  201.    modified our resources.  CURRENT is the widget before any resources
  202.    were changed; REQUEST is the widget before any class `set_values'
  203.    procedures have been called; NEW is the widget as updated by the
  204.    superclasses.
  205.  
  206.    We do not use the parameters ARGS and N_ARGS.  */
  207.  
  208. static Boolean
  209. bitmap_set_values (Widget current, Widget request, Widget new,
  210.                    ArgList args, Cardinal *n_args)
  211. {
  212.   BitmapWidget old_bw = (BitmapWidget) current;
  213.   bitmap_type *old_bitmap_ptr = (bitmap_type *) old_bw->bitmap.bits;
  214.   unsigned old_expansion = old_bw->bitmap.expansion;
  215.   BitmapWidget bw = (BitmapWidget) new;
  216.   bitmap_type *bitmap_ptr = (bitmap_type *) bw->bitmap.bits;
  217.   unsigned expansion = bw->bitmap.expansion;
  218.  
  219.   if (bitmap_ptr == NULL)
  220.     XtErrorMsg ("noData", "bitmapSetValues", "BitmapError",
  221.                 "No bitmap specified.", NULL, 0);
  222.  
  223.   if (bitmap_ptr == old_bitmap_ptr && expansion == old_expansion)
  224.     return False; /* No redisplay needed.  */
  225.  
  226.   {
  227.     Display *display = XtDisplay (bw);
  228.  
  229.     /* Our new width and height, in window coordinates.  */
  230.     unsigned w = BITMAP_WIDTH (*bitmap_ptr) * expansion;
  231.     unsigned h = BITMAP_HEIGHT (*bitmap_ptr) * expansion;
  232.  
  233.     /* The new image.  */
  234.     Pixmap pixmap = bitmap_to_pixmap (display, *bitmap_ptr, expansion);
  235.  
  236.     Arg label_args[]
  237.       = { { XtNbitmap,    pixmap },
  238.           { XtNwidth,    w },
  239.           { XtNheight,    h },
  240.         };
  241.  
  242.     /* If we were already displaying a pixmap, free it.  Perhaps it
  243.        would be better to keep it around, in case the user goes back
  244.        to this character.  */
  245.     if (bw->label.pixmap != None)
  246.       XFreePixmap (display, bw->label.pixmap);
  247.  
  248.     /* The bitmap has changed, so we should clear the `modified' field.  */
  249.     bw->bitmap.modified = false;
  250.  
  251.     /* Make the changes.  */
  252.     XtSetValues ((Widget) bw, XTARG (label_args));
  253.  
  254.     /* We want to be redisplayed now.  */
  255.     return True;
  256.   }
  257. }
  258.  
  259.  
  260. /* This routine is called when the widget is destroyed.  We deallocate
  261.    the GC resources we have explicitly created.  */
  262.  
  263. static void
  264. bitmap_destroy (Widget w)
  265. {
  266.   BitmapWidget bw = (BitmapWidget) w;
  267.   
  268.   XFreeGC (XtDisplay (bw), bw->bitmap.edit_gc);
  269.   XFreeGC (XtDisplay (bw), bw->bitmap.select_gc);
  270. }  
  271.  
  272.  
  273.  
  274. /* These convenience procedures save clients the trouble of constructing
  275.    an ArgList to get the resources in the Bitmap widget.  */
  276.  
  277. unsigned
  278. BitmapExpansion (Widget w)
  279. {
  280.   BitmapWidget bw = (BitmapWidget) w;
  281.   return bw->bitmap.expansion;
  282. }
  283.  
  284.  
  285. bitmap_type *
  286. BitmapBits (Widget w)
  287. {
  288.   BitmapWidget bw = (BitmapWidget) w;
  289.   return (bitmap_type *) bw->bitmap.bits;
  290. }
  291.  
  292.  
  293. Boolean
  294. BitmapModified (Widget w)
  295. {
  296.   BitmapWidget bw = (BitmapWidget) w;
  297.   return bw->bitmap.modified;
  298. }
  299.  
  300.  
  301.  
  302. /* This utility routine converts the bitmap B into a Pixmap on the
  303.    display DISPLAY.  Each pixel in B becomes EXPANSION pixels in the
  304.    Pixmap.  If the pixmap creation fails, it gives a fatal error.  */
  305.  
  306. #define DEPTH 1  /* We don't need grayscale or color bitmaps.  */
  307.  
  308. static Pixmap
  309. bitmap_to_pixmap (Display *display, bitmap_type b, unsigned expansion)
  310. {
  311.   GC gc;
  312.   XGCValues gc_values;
  313.   unsigned row;
  314.   XRectangle *save_rectangle_list;
  315.   XRectangle *rectangle_list = NULL;
  316.   unsigned nrectangles = 0;
  317.   
  318.   unsigned height = BITMAP_HEIGHT (b) * expansion;
  319.   unsigned w = BITMAP_WIDTH (b);
  320.   unsigned width = BITMAP_WIDTH (b) * expansion;
  321.   
  322.   /* We need a drawable to create the pixmap on.  Since we know we have
  323.      a root window, we don't have to be passed one.  */
  324.   Window root_window = DefaultRootWindow (display);
  325.   Pixmap pixmap = XCreatePixmap (display, root_window, width, height, DEPTH);
  326.  
  327.   if (pixmap == None)
  328.     FATAL ("bitmap_to_pixmap: Could not allocate pixmap");
  329.  
  330.   /* Start with the foreground zero (which is the default), since we
  331.      want to clear the pixmap before writing anything.  We want the
  332.      background zero so that the gc will correspond to the bitmap.  */
  333.   gc_values.background = 0;
  334.   gc = XCreateGC (display, pixmap, GCBackground, &gc_values);
  335.  
  336.   /* Write the bitmap to the pixmap.  We want the one bits in the bitmap
  337.      to end up as one bits in the pixmap, regardless of the actual
  338.      foreground and background pixels in the application.  Why?  Because
  339.      we (and the Label widget) use XCopyPlane to draw the pixmap, and
  340.      XCopyPlane turns one bits into the foreground color, and zero bits
  341.      in the background color.  */
  342.   XFillRectangle (display, pixmap, gc, 0, 0, width, height);
  343.   XSetForeground (display, gc, 1L);
  344.  
  345.   /* Instead of creating a separate rectangle for each pixel, we
  346.      combine adjacent pixels in each scanline into one rectangle.  We
  347.      could go further, and combine identical adjacent scanlines
  348.      (analysis has shown this happens about 1/3 of the time).  Or we
  349.      could go further still, and use some theory about decomposition
  350.      into a minimal set of rectangles, as cited in `Rectangular
  351.      Convolution for Fast Filtering of Characters', by Avi Naiman and
  352.      Alain Fournier, in the July 1987 SIGGRAPH Proceedings.  But doing
  353.      this simple job seems to make it fast enough.  */
  354.   for (row = 0; row < BITMAP_HEIGHT (b); row++)
  355.     {
  356.       unsigned start;
  357.       boolean done = false;
  358.       one_byte *row_data = BITMAP_ROW (b, row);
  359.       unsigned *transitions = bitmap_find_transitions (row_data, w);
  360.       unsigned *save_transitions = transitions;
  361.       
  362.       /* We must perform the test on `done' before dereferencing
  363.          `transitions' in the increment step of the loop, because if we
  364.          are indeed done, `transitions' may point to garbage.
  365.          bitmap_find_transitions guarantees an even number of
  366.          transitions.  */
  367.       for (start = *transitions++; start != w + 1;
  368.            start = done ? w + 1 : *transitions++)
  369.         {
  370.           XRectangle *r;
  371.           unsigned end = *transitions++;
  372.  
  373.           XRETALLOC (rectangle_list, ++nrectangles, XRectangle);
  374.           r = &rectangle_list[nrectangles - 1];
  375.           r->x = start * expansion;
  376.           r->y = row * expansion;
  377.           r->width = (end - start) * expansion;
  378.           r->height = expansion;
  379.         }
  380.       free (save_transitions);
  381.     }
  382.  
  383.   /* The protocol puts some limit on the number of rectangles that can
  384.      be filled in a single request.  It is not uncommon for us to have
  385.      thousands of rectangles in the list, either because of a
  386.      large font (e.g., cminch), or a high resolution.  */
  387.   #define MAX_NRECTANGLES 4000
  388.   save_rectangle_list = rectangle_list;
  389.   while (nrectangles > MAX_NRECTANGLES)
  390.     {
  391.       XFillRectangles (display, pixmap, gc, rectangle_list, MAX_NRECTANGLES);
  392.       nrectangles -= MAX_NRECTANGLES;
  393.       rectangle_list += MAX_NRECTANGLES;
  394.     }
  395.   XFillRectangles (display, pixmap, gc, rectangle_list, nrectangles);
  396.   XFreeGC (display, gc);
  397.   
  398.   free (save_rectangle_list);
  399.   
  400.   return pixmap;
  401. }
  402.  
  403.  
  404. /* Redraw the pixel at (COL,ROW) in the bitmap widget BW.  Update the
  405.    pixmap in the widget, and, if realized, the window.  */ 
  406.  
  407. static void
  408. redraw_pixel (BitmapWidget bw, one_byte bitmap_color,
  409.               long foreground_color, int row, int col)
  410. {
  411.   static long last_foreground = 0; /* XCreateGC's default.  */
  412.   Display *display = XtDisplay (bw);
  413.   unsigned expansion = bw->bitmap.expansion;
  414.   int expanded_row = row * expansion,
  415.       expanded_col = col * expansion;
  416.   bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
  417.   Pixmap label_pixmap = bw->label.pixmap;
  418.  
  419.   /* Update the bitmap structure.  */
  420.   BITMAP_PIXEL (*bitmap, row, col) = bitmap_color;
  421.  
  422.   /* Only set the foreground if it has changed, for efficiency.  */
  423.   if (foreground_color != last_foreground)
  424.     {
  425.       XSetForeground (display, bw->bitmap.edit_gc, foreground_color);
  426.       last_foreground = foreground_color;
  427.     }
  428.   XFillRectangle (display, label_pixmap, bw->bitmap.edit_gc,
  429.                   expanded_col, expanded_row, expansion, expansion);
  430.   bw->bitmap.modified = true;
  431.   
  432.   /* We may as well call XCopyPlane ourselves, instead of generating an
  433.      Expose event, since we have all the information necessary.  */
  434.   if (XtIsRealized ((Widget) bw))
  435.     XCopyPlane (display, label_pixmap, XtWindow (bw), bw->label.normal_GC,
  436.                 expanded_col, expanded_row, expansion, expansion,
  437.                 expanded_col, expanded_row, 1L);
  438. }
  439.  
  440.  
  441. /* Return the position of the pointer as recorded in EVENT.  */
  442.  
  443. static coordinate_type
  444. get_pointer_position (XEvent *event)
  445. {
  446.   coordinate_type c;
  447.   
  448.   switch (event->type)
  449.     {
  450.     case MotionNotify:
  451.       c.x = event->xmotion.x;
  452.       c.y = event->xmotion.y;
  453.       break;
  454.     
  455.     case ButtonPress:
  456.     case ButtonRelease:
  457.       c.x = event->xbutton.x;
  458.       c.y = event->xbutton.y;
  459.       break;
  460.     
  461.     case KeyPress:
  462.     case KeyRelease:
  463.       c.x = event->xkey.x;
  464.       c.y = event->xkey.y;
  465.       break;
  466.     
  467.     case EnterNotify:
  468.     case LeaveNotify:
  469.       c.x = event->xcrossing.x;
  470.       c.y = event->xcrossing.y;
  471.       break;
  472.     
  473.     default:
  474.       c.x = 0;
  475.       c.y = 0;
  476.     }
  477.   
  478.   return c;
  479. }
  480.  
  481.  
  482. /* If SEEN != EXPECTED, or if there is no bitmap in BW to operate on,
  483.    report an error for the action NAME.  */
  484.  
  485. static void
  486. check_action_parameters (string name, BitmapWidget bw, Cardinal seen,
  487.                          Cardinal expected)
  488. {
  489.   if (bw->bitmap.bits == NULL || bw->label.pixmap == None)
  490.     XtErrorMsg ("noData", name, "BitmapError",
  491.                 "No bitmap specified", NULL, 0);
  492.   
  493.   if (seen != expected)
  494.     {
  495.       string error_params[] = { itoa (expected), itoa (seen) };
  496.       unsigned n_error_params = XtNumber (error_params);
  497.       XtErrorMsg ("invalidParameters", name, "BitmapError",
  498.                   "Expected %s argument(s), found %s",
  499.                   error_params, &n_error_params);
  500.   }
  501. }    
  502.  
  503.  
  504. /* If V is not one of the elements in VALUE_LIST, abort with an error
  505.    message.  Otherwise, return the (zero-based) index of V in the list.
  506.    VALUE_LIST should end with an element that is NULL.  */
  507.  
  508. static unsigned
  509. param_value (string v, string value_list[])
  510. {
  511.   unsigned ret = 0;
  512.   
  513.   while (value_list[ret] != NULL && !STREQ (value_list[ret], v))
  514.     ret++;
  515.  
  516.   /* Did we find it?  */
  517.   if (value_list[ret] == NULL)
  518.     {
  519.       unsigned this_value;
  520.       unsigned n_error_params = 2;
  521.       string error_params[n_error_params];
  522.       
  523.       error_params[0] = *value_list;
  524.       for (this_value = 1; this_value < ret; this_value++)
  525.         {
  526.           string temp = error_params[0];
  527.           error_params[0] = concat3 (error_params[0], ",",
  528.                                      value_list[this_value]);
  529.           free (temp);
  530.         }
  531.       
  532.       error_params[1] = v;
  533.  
  534.       XtErrorMsg ("invalidParameter", "acceptPaste", "BitmapError",
  535.                   "Expected one of (%s), found `%s'",
  536.                   error_params, &n_error_params);
  537.     }
  538.   
  539.   return ret;
  540. }
  541.  
  542.  
  543. /* Given a position in window coordinates, we sometimes need to
  544.    translate it to an expanded pixel boundary.  We want to do this via
  545.    rounding, instead of truncating, so that the user can select the
  546.    pixels in the rightmost column and bottommost row of the bitmap.
  547.    
  548.    On the other hand, sometimes we want to truncate the position,
  549.    instead of rounding, so that the starting positions are intuitive.
  550.    
  551.    We assume that the variable `bw' points to a bitmap widget.  */
  552.  
  553. #define EXPANSION_ROUND(n)                        \
  554.   (bw->bitmap.expansion                            \
  555.    * (((n) + bw->bitmap.expansion - 1) / bw->bitmap.expansion))
  556.  
  557. #define EXPANSION_TRUNC(n)                        \
  558.   (bw->bitmap.expansion * ((n) / bw->bitmap.expansion))
  559.  
  560.  
  561. /* Return whether the cursor position in E is within the bitmap in BW.
  562.    We assume that the bitmap is non-null.  In any case, return the row
  563.    and column in window coordinates in ROW and COL.  */
  564.  
  565. static boolean
  566. find_bitmap_position (XEvent *e, BitmapWidget bw, int *row, int *col)
  567. {
  568.   coordinate_type pos = get_pointer_position (e);
  569.   bitmap_type b = *(bitmap_type *) bw->bitmap.bits;
  570.   
  571.   /* Convert from the window coordinates to the pixel in the bitmap.  */
  572.   int bitmap_row = pos.y / bw->bitmap.expansion;
  573.   int bitmap_col = pos.x / bw->bitmap.expansion;
  574.   
  575.   *row = pos.y;
  576.   *col = pos.x;
  577.  
  578.   /* Return whether the row and column are outside the boundaries of the
  579.      bitmap.  */
  580.   return pos.y >= 0 && bitmap_row < BITMAP_HEIGHT (b)
  581.     && pos.x >= 0 && bitmap_col < BITMAP_WIDTH (b);
  582. }
  583.  
  584.  
  585.  
  586. /* Action procedures.  */
  587.  
  588. /* N_PARAMS should be exactly one, and PARAMS should point to either the
  589.    string `Continuous' or `Discrete' (case is significant).  If the
  590.    latter, we simply invert the pixel at the current mouse location, as
  591.    specified in EVENT.  If the former, we only do the inversion if we
  592.    are at a different pixel on this call than we were on the previous
  593.    one.  */
  594.  
  595. static void
  596. invert_pixel (Widget w, XEvent *event, String *params, Cardinal *n_params)
  597. {
  598.   static int last_row = -1; /* Expanded pixel position of the last event.  */
  599.   static int last_col = -1;
  600.  
  601.   /* This is the color of the pixel at the pointer position at the time
  602.      of the last `Discrete' call.  */
  603.   static one_byte source_pixel;
  604.  
  605.   int row, col;
  606.   
  607.   one_byte target_color;
  608.   long foreground_color;
  609.   
  610.   BitmapWidget bw = (BitmapWidget) w;
  611.   bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
  612.   BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
  613.  
  614.   check_action_parameters ("invertPixel", bw, *n_params, 1);
  615.   if (!find_bitmap_position (event, bw, &row, &col))
  616.     return;
  617.   
  618.   row /= bw->bitmap.expansion;
  619.   col /= bw->bitmap.expansion;
  620.  
  621.   if (STREQ (*params, "Continuous"))  
  622.     { /* If we are still within the same (expanded) pixel, do nothing.  */
  623.       if (col == last_col && row == last_row)
  624.         return;
  625.  
  626.       /* We've moved, so do the inversion.  */
  627.       last_col = col;
  628.       last_row = row;
  629.     }
  630.   else if (STREQ (*params, "Discrete"))
  631.     {
  632.       /* Update `last_row' and `last_col' here, because it is easy to
  633.          jiggle (inadvertently) the mouse a little after clicking.  The
  634.          jiggling will cause a motion event that will invoke us again with
  635.          `Continuous' as the parameter.  If we didn't update the current
  636.          position here, that invocation would invert the pixel again.  */
  637.       last_col = col;
  638.       last_row = row;
  639.       source_pixel = BITMAP_PIXEL (*bitmap, row, col);
  640.     }
  641.   else
  642.     {
  643.       unsigned n_error_params = 1;
  644.       string error_params[1] = { *params };
  645.       XtErrorMsg ("invalidParameter", "invertPixel", "BitmapError",
  646.                   "Expected `Continuous' or `Discrete', found `%s'",
  647.                   error_params, &n_error_params);
  648.     }
  649.  
  650.   /* See comments in `bitmap_to_pixmap' for why the constants 1 and 0
  651.      are appropriate here.  We depend on getting a `Discrete' event
  652.      before any `Continuous' events; otherwise, `target_color' might be
  653.      garbage.
  654.      
  655.      We are not testing the color of the pixel at the current
  656.      position here.  Why?  Because we want to change pixels on
  657.      `Continuous' events to a single color, not inverting them.  See
  658.      `Bitmap.h' for more discussion of this.  */
  659.   if (source_pixel == WHITE)
  660.     { /* Make it black.  */
  661.       target_color = BLACK;
  662.       foreground_color = 1;
  663.     }
  664.   else
  665.     { /* Make it white.  */
  666.       target_color = WHITE;
  667.       foreground_color = 0;
  668.     }
  669.   
  670.   /* Update ourselves.  */
  671.   redraw_pixel (bw, target_color, foreground_color, row, col);
  672.  
  673.   /* Update our shadow bitmap, if we have one.  */
  674.   if (shadow_bw != NULL)
  675.     redraw_pixel (shadow_bw, target_color, foreground_color, row, col);
  676. }
  677.  
  678.  
  679. /* Fill the selection in the bitmap widget W with the color of the pixel
  680.    the pointer is on, as recorded in EVENT.  N_PARAMS should be zero.  */ 
  681.    
  682. static void
  683. fill_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
  684. {
  685.   int row, col; /* The coordinates in the bitmap.  */
  686.   BitmapWidget bw = (BitmapWidget) w;
  687.  
  688.   check_action_parameters ("fillSelection", bw, *n_params, 0);
  689.   if (bw->bitmap.selection != NULL
  690.       && bw->bitmap.select_width > 0 && bw->bitmap.select_height > 0
  691.       && find_bitmap_position (event, bw, &row, &col))
  692.     {
  693.       bitmap_type new;
  694.       bitmap_type *bitmap = (bitmap_type *) bw->bitmap.bits;
  695.       one_byte color = BITMAP_PIXEL (*bitmap, row / bw->bitmap.expansion,
  696.                                      col / bw->bitmap.expansion);
  697.       bounding_box_type select_bb = find_selection_bb (bw);
  698.  
  699.       for (row = MIN_ROW (select_bb); row <= MAX_ROW (select_bb); row++)
  700.         for (col = MIN_COL (select_bb); col < MAX_COL (select_bb); col++)
  701.           BITMAP_PIXEL (*bitmap, row, col) = color;
  702.       
  703.       bw->bitmap.modified = true;
  704.  
  705.       new = extract_subbitmap (*bitmap, select_bb);
  706.       redraw_selection (bw, new);
  707.       free_bitmap (&new);
  708.     }
  709. }
  710.  
  711.  
  712. /* Prepare to select a rectangle whose upper left corner is the pixel
  713.    enclosing the position recorded in EVENT.  N_PARAMS should be zero.  */
  714.  
  715. static void
  716. start_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
  717. {
  718.   int row, col; /* The coordinates in the bitmap.  */
  719.   BitmapWidget bw = (BitmapWidget) w;
  720.  
  721.   check_action_parameters ("startSelection", bw, *n_params, 0);
  722.   if (find_bitmap_position (event, bw, &row, &col))
  723.     {
  724.       /* Set the upper left corner and initialize the lower right corner to
  725.          the current position.  */
  726.       bw->bitmap.initial_select.x = bw->bitmap.select_ul.x =
  727.         EXPANSION_TRUNC (col);
  728.       bw->bitmap.initial_select.y = bw->bitmap.select_ul.y = 
  729.         EXPANSION_TRUNC (row);
  730.       bw->bitmap.select_width = bw->bitmap.select_height = 0;
  731.     }
  732. }
  733.  
  734.  
  735. /* Take the point in EVENT as the new second point to define the
  736.    selection.  */
  737.  
  738. static void
  739. adjust_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
  740. {
  741.   int row, col; /* The coordinates in the bitmap.  */
  742.   BitmapWidget bw = (BitmapWidget) w;
  743.  
  744.   check_action_parameters ("adjustSelection", bw, *n_params, 0);
  745.   unshow_selection (bw);
  746.  
  747.   if (find_bitmap_position (event, bw, &row, &col))
  748.     { 
  749.       update_selection (bw, row, col);
  750.       show_selection (bw);
  751.       selection_showing = true;
  752.     }
  753. }
  754.  
  755.  
  756. /* Take the point in EVENT to be the definitive second point for the
  757.    selection.  We therefore can now set the (sub)bitmap that the selected
  758.    rectangle defines, for pasting.  */
  759.  
  760. static void
  761. accept_selection (Widget w, XEvent *event, String *params, Cardinal *n_params)
  762. {
  763.   int row, col; /* The coordinates in the bitmap.  */
  764.   BitmapWidget bw = (BitmapWidget) w;
  765.  
  766.   check_action_parameters ("acceptSelection", bw, *n_params, 0);
  767.   unshow_selection (bw);
  768.  
  769.   /* If the selection is empty, do nothing.  Do not clear out the
  770.      previous selection, even.  */
  771.   if (bw->bitmap.select_width > 0 && bw->bitmap.select_height > 0
  772.       && find_bitmap_position (event, bw, &row, &col))
  773.     {
  774.       bounding_box_type select_bb;
  775.  
  776.       /* Set the final coordinates.  */
  777.       update_selection (bw, row, col);
  778.  
  779.       /* Free the old bitmap.  */
  780.       if (bw->bitmap.selection != NULL)
  781.         free_bitmap (bw->bitmap.selection);
  782.  
  783.       /* Make the new one.  */
  784.       select_bb = find_selection_bb (bw);
  785.       bw->bitmap.selection = xmalloc (sizeof (bitmap_type));
  786.       *(bw->bitmap.selection)
  787.         = extract_subbitmap (*(bitmap_type *) bw->bitmap.bits, select_bb);
  788.     }
  789. }
  790.  
  791.  
  792. /* If something has been selected, show its bounding rectangle with the
  793.    upper left corner at the position in EVENT (so the user can see where
  794.    the paste will happen).  */
  795.  
  796. static void
  797. start_paste (Widget w, XEvent *event, String *params, Cardinal *n_params)
  798. {
  799.   int row, col; /* The coordinates in the bitmap.  */
  800.   BitmapWidget bw = (BitmapWidget) w;
  801.  
  802.   check_action_parameters ("startPaste", bw, *n_params, 0);
  803.   /* We obviously can't paste anything if nothing has been selected.
  804.      Perhaps it would be better to show a warning message in that case,
  805.      but for now we just do nothing.  */
  806.   if (bw->bitmap.selection != NULL
  807.       && find_bitmap_position (event, bw, &row, &col))
  808.     {
  809.       bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
  810.       bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
  811.       show_selection (bw);
  812.       selection_showing = true;
  813.     }
  814. }
  815.  
  816.  
  817. /* Update where the paste rectangle is according to EVENT.  */
  818.  
  819. static void
  820. move_paste (Widget w, XEvent *event, String *params, Cardinal *n_params)
  821. {
  822.   int row, col; /* The coordinates in the bitmap.  */
  823.   BitmapWidget bw = (BitmapWidget) w;
  824.  
  825.   check_action_parameters ("movePaste", bw, *n_params, 0);
  826.   unshow_selection (bw);
  827.  
  828.   if (bw->bitmap.selection != NULL
  829.       && find_bitmap_position (event, bw, &row, &col))
  830.     {
  831.       bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
  832.       bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
  833.       show_selection (bw); /* Show the new.  */
  834.       selection_showing = true;
  835.     }
  836. }
  837.  
  838.  
  839. /* Take the position in EVENT as the place to paste the selection, and
  840.    update the bitmap.  Either ignore or keep the black in the bitmap
  841.    underneath the paste according to first element of PARAMS, which must
  842.    be either `Opaque' or `Transparent'.  We flip the bitmap vertically,
  843.    horizontally, both, or neither according to the second parameter
  844.    being `FlipVertical', `FlipHorizontal', `FlipBoth', or `FlipNeither'.  */
  845.  
  846. static void
  847. accept_paste (Widget w, XEvent *event, String *params, Cardinal *n_params) 
  848. {
  849.   coordinate_type paste; /* The coordinates in the bitmap.  */
  850.   int row, col;
  851.   BitmapWidget bw = (BitmapWidget) w;
  852.  
  853.   check_action_parameters ("acceptPaste", bw, *n_params, 2);
  854.   unshow_selection (bw);
  855.  
  856.   if (bw->bitmap.selection != NULL
  857.       && find_bitmap_position (event, bw, &row, &col))
  858.     {
  859.       enum { flip_neither, flip_horizontal, flip_vertical, flip_both } flip;
  860.       boolean opaque;
  861.       unsigned source_row, source_col;
  862.       unsigned target_row, target_col;
  863.       unsigned this_row, this_col;
  864.       string param1_values[]
  865.         = { "Transparent", "Opaque", NULL };
  866.       string param2_values[]
  867.         = { "FlipNeither", "FlipHorizontal", "FlipVertical", "FlipBoth",
  868.             NULL };
  869.  
  870.       bitmap_type source = *(bw->bitmap.selection);
  871.       unsigned source_height = BITMAP_HEIGHT (source),
  872.                source_width = BITMAP_WIDTH (source);
  873.       bitmap_type *target = (bitmap_type *) bw->bitmap.bits;
  874.       unsigned target_height = BITMAP_HEIGHT (*target),
  875.                target_width = BITMAP_WIDTH (*target);
  876.       
  877.       /* Get the parameters we're passed.  */
  878.       opaque = param_value (params[0], param1_values);
  879.       flip = param_value (params[1], param2_values);
  880.  
  881.       /* Figure out where we are.  */
  882.       paste.x = col / bw->bitmap.expansion;
  883.       paste.y = row / bw->bitmap.expansion;
  884.  
  885.       /* Update the underlying bitmap.  Copy the black pixels from
  886.          the original if we're pasting transparently.  */
  887.       for (this_row = 0, target_row = paste.y;
  888.            this_row < source_height && target_row < target_height; 
  889.            this_row++, target_row++)
  890.         {
  891.           source_row = (flip == flip_horizontal || flip == flip_neither)
  892.                        ? this_row 
  893.                        : source_height - this_row - 1;
  894.  
  895.           for (this_col = 0, target_col = paste.x;
  896.                this_col < source_width && target_col < target_width;
  897.                this_col++, target_col++)
  898.             {
  899.               source_col = (flip == flip_vertical || flip == flip_neither)
  900.                            ? this_col
  901.                            : source_width - this_col - 1;
  902.                            
  903.               BITMAP_PIXEL (*target, target_row, target_col)
  904.                 = BITMAP_PIXEL (source, source_row, source_col)
  905.                   | (opaque
  906.                      ? WHITE
  907.                      : BITMAP_PIXEL (*target, target_row, target_col));
  908.             }
  909.         }
  910.  
  911.       bw->bitmap.modified = true;
  912.  
  913.       /* If necessary, get the combined selection image back.  */
  914.       if (!opaque || flip != flip_neither)
  915.         {
  916.           bounding_box_type select_bb = find_selection_bb (bw);
  917.           source = extract_subbitmap (*target, select_bb);
  918.         }
  919.  
  920.       redraw_selection (bw, source);
  921.  
  922.       if (!opaque)
  923.         free_bitmap (&source);
  924.     }
  925. }
  926.  
  927.  
  928.  
  929. /* Convert the selection, which is in window coordinates, to a bounding
  930.    box, in bitmap coordinates.  */
  931.  
  932. static bounding_box_type
  933. find_selection_bb (BitmapWidget bw)
  934. {
  935.   bounding_box_type select_bb;
  936.   unsigned expansion = bw->bitmap.expansion;
  937.   
  938.   MIN_COL (select_bb) = bw->bitmap.select_ul.x / expansion;
  939.   MIN_ROW (select_bb) = bw->bitmap.select_ul.y / expansion;
  940.   MAX_COL (select_bb)
  941.     = MIN_COL (select_bb) + bw->bitmap.select_width / expansion;
  942.   MAX_ROW (select_bb)
  943.     = MIN_ROW (select_bb) + bw->bitmap.select_height / expansion;
  944.  
  945.   /* Because of the asymmetry of bounding boxes, we must
  946.      subtract one from the row.  */
  947.   MAX_ROW (select_bb)--;
  948.   
  949.   return select_bb;
  950. }
  951.  
  952.           
  953. /* Redraw the area in the bitmap widget BW given by the selection.  The
  954.    new appearance of the area is given as a bitmap in SOURCE.  Also
  955.    redraw the shadow bitmap, if it exists.  */
  956.  
  957. #define COMPUTE_SHADOW_COORD(field)                    \
  958.   shadow_bw->bitmap.field = shadow_bw->bitmap.expansion            \
  959.                             * bw->bitmap.field / bw->bitmap.expansion;
  960.  
  961. static void
  962. redraw_selection (BitmapWidget bw, bitmap_type source)
  963. {
  964.   BitmapWidget shadow_bw = (BitmapWidget) bw->bitmap.shadow;
  965.  
  966.   redraw_selection_bitmap (bw, source);
  967.  
  968.   if (shadow_bw != NULL)
  969.     {
  970.       COMPUTE_SHADOW_COORD (select_ul.x);
  971.       COMPUTE_SHADOW_COORD (select_ul.y);
  972.       COMPUTE_SHADOW_COORD (select_width);
  973.       COMPUTE_SHADOW_COORD (select_height);
  974.       redraw_selection_bitmap (shadow_bw, source);
  975.     }
  976. }
  977.  
  978. static void
  979. redraw_selection_bitmap (BitmapWidget bw, bitmap_type source)
  980. {
  981.   Display *display = XtDisplay (bw);
  982.   Pixmap selection_pixmap = bitmap_to_pixmap (display, source,
  983.                                               bw->bitmap.expansion);
  984.  
  985.   /* Update the pixmap in our parent label.  */
  986.   XCopyArea (XtDisplay (bw), selection_pixmap, bw->label.pixmap,
  987.              bw->label.normal_GC, 0, 0, 
  988.              bw->bitmap.select_width, bw->bitmap.select_height,
  989.              bw->bitmap.select_ul.x, bw->bitmap.select_ul.y);
  990.   XFreePixmap (display, selection_pixmap);
  991.  
  992.   /* Update the display.  */
  993.   if (XtIsRealized ((Widget) bw))
  994.     /* Server bug?  On my machine, this doesn't always update large pastes
  995.        correctly; some parts of the window are left.  */
  996. #if 0
  997.     XCopyPlane (display, bw->label.pixmap, XtWindow (bw), 
  998.                 bw->label.normal_GC, 
  999.                 bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
  1000.                 bw->bitmap.select_width, bw->bitmap.select_height,
  1001.                 bw->bitmap.select_ul.x, bw->bitmap.select_ul.y, 1L);
  1002. #endif
  1003.     XClearArea (display, XtWindow (bw),
  1004.                 bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
  1005.                 bw->bitmap.select_width, bw->bitmap.select_height,
  1006.                 True); /* Generate Expose event.  */
  1007. }
  1008.  
  1009.  
  1010. /* If we are realized, display the currently selected rectangle.  */
  1011.  
  1012. static void
  1013. show_selection (BitmapWidget bw)
  1014. {
  1015.   if (XtIsRealized ((Widget) bw))
  1016.     XDrawRectangle (XtDisplay (bw), XtWindow (bw), bw->bitmap.select_gc, 
  1017.                     bw->bitmap.select_ul.x, bw->bitmap.select_ul.y,
  1018.                     bw->bitmap.select_width, bw->bitmap.select_height);
  1019. }
  1020.  
  1021.  
  1022. /* If the user moves off the bitmap, we need to erase the last
  1023.    selection rectangle we drew.  */
  1024.  
  1025. static void
  1026. unshow_selection (BitmapWidget bw)
  1027. {
  1028.   if (selection_showing)
  1029.     {
  1030.       show_selection (bw);
  1031.       selection_showing = false;
  1032.     }
  1033. }
  1034.  
  1035.  
  1036. /* Do the appropriate maneuvers for the new row ROW and column COL for
  1037.    computing the upper left corner of the selection.  These numbers are
  1038.    in unrounded window coordinates, i.e., they may not lie on pixel
  1039.    boundaries. If we think of the initial selection point as the origin,
  1040.    the new point can be in any of the four quadrants.  */
  1041.  
  1042. #define initial_x (bw->bitmap.initial_select.x)
  1043. #define initial_y (bw->bitmap.initial_select.y)
  1044.  
  1045. static void
  1046. update_selection (BitmapWidget bw, unsigned row, unsigned col)
  1047. {
  1048.   if (col < initial_x)
  1049.     {
  1050.       bw->bitmap.select_ul.x = EXPANSION_TRUNC (col);
  1051.       bw->bitmap.select_width = EXPANSION_ROUND (initial_x - col);
  1052.     }
  1053.   else
  1054.     {
  1055.       bw->bitmap.select_ul.x = initial_x;
  1056.       bw->bitmap.select_width = EXPANSION_ROUND (col - initial_x);
  1057.     }
  1058.  
  1059.   if (row < initial_y)
  1060.     {
  1061.       bw->bitmap.select_ul.y = EXPANSION_TRUNC (row);
  1062.       bw->bitmap.select_height = EXPANSION_ROUND (initial_y - row);
  1063.     }
  1064.   else
  1065.     {
  1066.       bw->bitmap.select_ul.y = initial_y;
  1067.       bw->bitmap.select_height = EXPANSION_ROUND (row - initial_y);
  1068.     }
  1069. }
  1070.