Bitmap.c revision 2b32c8f7
1/*
2
3Copyright 1989, 1998  The Open Group
4
5Permission to use, copy, modify, distribute, and sell this software and its
6documentation for any purpose is hereby granted without fee, provided that
7the above copyright notice appear in all copies and that both that
8copyright notice and this permission notice appear in supporting
9documentation.
10
11The above copyright notice and this permission notice shall be included
12in all copies or substantial portions of the Software.
13
14THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
15OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR
18OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
19ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
20OTHER DEALINGS IN THE SOFTWARE.
21
22Except as contained in this notice, the name of The Open Group shall
23not be used in advertising or otherwise to promote the sale, use or
24other dealings in this Software without prior written authorization
25from The Open Group.
26
27*/
28
29/*
30 * Author:  Davor Matic, MIT X Consortium
31 */
32
33#include <X11/IntrinsicP.h>
34#include <X11/StringDefs.h>
35#include <X11/Xaw/XawInit.h>
36#include <X11/Xmu/CharSet.h>
37#include <X11/Xmu/Drawing.h>
38#include <X11/Xmu/SysUtil.h>
39#include <X11/Xatom.h>
40#include <X11/Xfuncs.h>
41#include <X11/Xos.h>
42#include "BitmapP.h"
43#include "Bitmap.h"
44
45#include <stdio.h>
46#include <math.h>
47
48#define min(x, y)                     ((((int)(x)) < (int)(y)) ? (x) : (y))
49#define max(x, y)                     ((((int)(x)) > (int)(y)) ? (x) : (y))
50
51Boolean DEBUG;
52
53#define DefaultGridTolerance 8
54#define DefaultBitmapSize    "16x16"
55#define FallbackBitmapWidth  16
56#define FallbackBitmapHeight 16
57#define DefaultGrid          TRUE
58#define DefaultDashed        TRUE
59#define DefaultStippled      TRUE
60#define DefaultProportional  TRUE
61#define DefaultAxes          FALSE
62#define DefaultMargin        16
63#define DefaultSquareWidth   16
64#define DefaultSquareHeight  16
65#define DefaultFilename      ""
66
67#define Offset(field) XtOffsetOf(BitmapRec, bitmap.field)
68
69static XtResource resources[] = {
70{XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel),
71     Offset(foreground_pixel), XtRString, XtDefaultForeground},
72{XtNhighlight, XtCHighlight, XtRPixel, sizeof(Pixel),
73     Offset(highlight_pixel), XtRString, XtDefaultForeground},
74{XtNframe, XtCFrame, XtRPixel, sizeof(Pixel),
75     Offset(frame_pixel), XtRString, XtDefaultForeground},
76{XtNgridTolerance, XtCGridTolerance, XtRDimension, sizeof(Dimension),
77     Offset(grid_tolerance), XtRImmediate, (XtPointer) DefaultGridTolerance},
78{XtNsize, XtCSize, XtRString, sizeof(String),
79     Offset(size), XtRImmediate, (XtPointer) DefaultBitmapSize},
80{XtNdashed, XtCDashed, XtRBoolean, sizeof(Boolean),
81     Offset(dashed), XtRImmediate, (XtPointer) DefaultDashed},
82{XtNgrid, XtCGrid, XtRBoolean, sizeof(Boolean),
83     Offset(grid), XtRImmediate, (XtPointer) DefaultGrid},
84{XtNstippled, XtCStippled, XtRBoolean, sizeof(Boolean),
85     Offset(stippled), XtRImmediate, (XtPointer) DefaultStippled},
86{XtNproportional, XtCProportional, XtRBoolean, sizeof(Boolean),
87     Offset(proportional), XtRImmediate, (XtPointer) DefaultProportional},
88{XtNaxes, XtCAxes, XtRBoolean, sizeof(Boolean),
89     Offset(axes), XtRImmediate, (XtPointer) DefaultAxes},
90{XtNsquareWidth, XtCSquareWidth, XtRDimension, sizeof(Dimension),
91     Offset(squareW), XtRImmediate, (XtPointer) DefaultSquareWidth},
92{XtNsquareHeight, XtCSquareHeight, XtRDimension, sizeof(Dimension),
93     Offset(squareH), XtRImmediate, (XtPointer) DefaultSquareHeight},
94{XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension),
95     Offset(margin), XtRImmediate, (XtPointer) DefaultMargin},
96{XtNxHot, XtCXHot, XtRPosition, sizeof(Position),
97     Offset(hot.x), XtRImmediate, (XtPointer) NotSet},
98{XtNyHot, XtCYHot, XtRPosition, sizeof(Position),
99     Offset(hot.y), XtRImmediate, (XtPointer) NotSet},
100{XtNbutton1Function, XtCButton1Function, XtRButtonFunction, sizeof(int),
101     Offset(button_function[0]), XtRImmediate, (XtPointer) Set},
102{XtNbutton2Function, XtCButton2Function, XtRButtonFunction, sizeof(int),
103     Offset(button_function[1]), XtRImmediate, (XtPointer) Invert},
104{XtNbutton3Function, XtCButton3Function, XtRButtonFunction, sizeof(int),
105     Offset(button_function[2]), XtRImmediate, (XtPointer) Clear},
106{XtNbutton4Function, XtCButton4Function, XtRButtonFunction, sizeof(int),
107     Offset(button_function[3]), XtRImmediate, (XtPointer) Clear},
108{XtNbutton5Function, XtCButton5Function, XtRButtonFunction, sizeof(int),
109     Offset(button_function[4]), XtRImmediate, (XtPointer) Clear},
110{XtNfilename, XtCFilename, XtRString, sizeof(String),
111     Offset(filename), XtRImmediate, (XtPointer) DefaultFilename},
112{XtNbasename, XtCBasename, XtRString, sizeof(String),
113     Offset(basename), XtRImmediate, (XtPointer) DefaultFilename},
114{XtNdashes, XtCDashes, XtRBitmap, sizeof(Pixmap),
115     Offset(dashes), XtRImmediate, (XtPointer) XtUnspecifiedPixmap},
116{XtNstipple, XtCStipple, XtRBitmap, sizeof(Pixmap),
117     Offset(stipple), XtRImmediate, (XtPointer) XtUnspecifiedPixmap},
118};
119#undef Offset
120
121
122static XtActionsRec actions[] =
123{
124{"mark",               (XtActionProc)BWTMark},
125{"mark-all",           (XtActionProc)BWTMarkAll},
126{"unmark",             (XtActionProc)BWTUnmark},
127{"paste",              (XtActionProc)BWTPaste},
128{"bw-debug",           (XtActionProc)BWDebug},
129{"abort",              (XtActionProc)BWAbort},
130{"store-to-buffer",    (XtActionProc)BWStoreToBuffer},
131{"change-notify",      (XtActionProc)BWChangeNotify},
132{"set-changed",        (XtActionProc)BWSetChanged},
133{"up",                 (XtActionProc)BWUp},
134{"down",               (XtActionProc)BWDown},
135{"left",               (XtActionProc)BWLeft},
136{"right",              (XtActionProc)BWRight},
137{"fold",               (XtActionProc)BWFold},
138{"flip-horiz",         (XtActionProc)BWFlipHoriz},
139{"flip-vert",          (XtActionProc)BWFlipVert},
140{"rotate-right",       (XtActionProc)BWRotateRight},
141{"rotate-left",        (XtActionProc)BWRotateLeft},
142{"set",                (XtActionProc)BWSet},
143{"clear",              (XtActionProc)BWClear},
144{"invert",             (XtActionProc)BWInvert},
145{"undo",               (XtActionProc)BWUndo},
146{"redraw",             (XtActionProc)BWRedraw},
147};
148
149static char translations1[] =
150"\
151Shift<Btn1Down>: mark()\n\
152Shift<Btn2Down>: mark-all()\n\
153Shift<Btn3Down>: unmark()\n\
154Ctrl<BtnDown>:   paste()\n\
155Ctrl<Key>l: redraw()\n\
156<Key>d:     bw-debug()\n\
157<Key>a:     abort()\n\
158<Key>Up:    store-to-buffer()\
159            up()\
160            change-notify()\
161            set-changed()\n\
162<Key>KP_Up: store-to-buffer()\
163            up()\
164            change-notify()\
165            set-changed()\n\
166<Key>Down:  store-to-buffer()\
167            down()\
168            change-notify()\
169            set-changed()\n\
170<Key>KP_Down: store-to-buffer()\
171            down()\
172            change-notify()\
173            set-changed()\n\
174<Key>Left:  store-to-buffer()\
175            left()\
176            change-notify()\
177            set-changed()\n\
178<Key>KP_Left: store-to-buffer()\
179            left()\
180            change-notify()\
181            set-changed()\n\
182<Key>Right: store-to-buffer()\
183            right()\
184            change-notify()\
185            set-changed()\n\
186<Key>KP_Right: store-to-buffer()\
187            right()\
188            change-notify()\
189            set-changed()\n\
190<Key>f:     store-to-buffer()\
191            fold()\
192            change-notify()\
193            set-changed()\n\
194<Key>h:     store-to-buffer()\
195            flip-horiz()\
196            change-notify()\
197            set-changed()\n\
198";
199
200static char translations2[] =
201"<Key>v:     store-to-buffer()\
202            flip-vert()\
203            change-notify()\
204            set-changed()\n\
205<Key>r:     store-to-buffer()\
206            rotate-right()\
207            change-notify()\
208            set-changed()\n\
209<Key>l:     store-to-buffer()\
210            rotate-left()\
211            change-notify()\
212            set-changed()\n\
213<Key>s:     store-to-buffer()\
214            set()\
215            change-notify()\
216            set-changed()\n\
217<Key>c:     store-to-buffer()\
218            clear()\
219            change-notify()\
220            set-changed()\n\
221<Key>i:     store-to-buffer()\
222            invert()\
223            change-notify()\
224            set-changed()\n\
225<Key>u:     undo()\
226            change-notify()\
227            set-changed()\n\
228";
229
230static Atom targets[] = {
231    XA_BITMAP,
232    XA_PIXMAP
233};
234
235#include "Requests.h"
236
237
238static BWRequestRec requests[] =
239{
240{MarkRequest, sizeof(BWStatus),
241     TwoPointsEngage, (XtPointer) BWDrawRectangle,
242     TwoPointsTerminateTimed, (XtPointer) BWSelect,
243     NULL, (XtPointer) NULL},
244{RestoreRequest, sizeof(BWStatus),
245     OnePointEngage, (XtPointer) BWDragStored,
246     OnePointTerminate, (XtPointer) BWRestore,
247     NULL, (XtPointer) NULL},
248{ImmediateCopyRequest, sizeof(BWStatus),
249     OnePointEngage, (XtPointer) BWDragMarked,
250     OnePointTerminate, (XtPointer) BWCopy,
251     NULL, (XtPointer) NULL},
252{ImmediateMoveRequest, sizeof(BWStatus),
253     OnePointEngage, (XtPointer) BWDragMarked,
254     OnePointTerminate, (XtPointer) BWMove,
255     NULL, (XtPointer) NULL},
256{CopyRequest, sizeof(BWStatus),
257     DragOnePointEngage, (XtPointer) Paste,
258     DragOnePointTerminate, (XtPointer) ImmediateCopyRequest,
259     Interface, (XtPointer) BWUnmark},
260{MoveRequest, sizeof(BWStatus),
261     DragOnePointEngage, (XtPointer) Paste,
262     DragOnePointTerminate, (XtPointer) ImmediateMoveRequest,
263     Interface, (XtPointer) BWUnmark},
264{PointRequest, sizeof(BWStatus),
265     DragOnePointEngage, (XtPointer) BWDrawPoint,
266     DragOnePointTerminate, (XtPointer) BWDrawPoint,
267     NULL, (XtPointer) NULL},
268{CurveRequest, sizeof(BWStatus),
269     DragTwoPointsEngage, (XtPointer) BWBlindLine,
270     DragTwoPointsTerminate, (XtPointer) BWBlindLine,
271     NULL, (XtPointer) NULL},
272{LineRequest, sizeof(BWStatus),
273     TwoPointsEngage, (XtPointer) BWDrawLine,
274     TwoPointsTerminate, (XtPointer) BWDrawLine,
275     NULL, (XtPointer) NULL},
276{RectangleRequest, sizeof(BWStatus),
277     TwoPointsEngage, (XtPointer) BWDrawRectangle,
278     TwoPointsTerminate, (XtPointer) BWDrawRectangle,
279     NULL, (XtPointer) NULL},
280{FilledRectangleRequest, sizeof(BWStatus),
281     TwoPointsEngage, (XtPointer) BWDrawRectangle,
282     TwoPointsTerminate, (XtPointer) BWDrawFilledRectangle,
283     NULL, (XtPointer) NULL},
284{CircleRequest, sizeof(BWStatus),
285     TwoPointsEngage, (XtPointer) BWDrawCircle,
286     TwoPointsTerminate, (XtPointer) BWDrawCircle,
287     NULL, (XtPointer) NULL},
288{FilledCircleRequest, sizeof(BWStatus),
289     TwoPointsEngage, (XtPointer) BWDrawCircle,
290     TwoPointsTerminate, (XtPointer) BWDrawFilledCircle,
291     NULL, (XtPointer) NULL},
292{FloodFillRequest, sizeof(BWStatus),
293     OnePointEngage, (XtPointer) NULL,
294     OnePointTerminate, (XtPointer) BWFloodFill,
295     NULL, (XtPointer) NULL},
296{HotSpotRequest, sizeof(BWStatus),
297     OnePointEngage, (XtPointer) BWDrawHotSpot,
298     OnePointTerminate, (XtPointer) BWDrawHotSpot,
299     NULL, (XtPointer) NULL},
300{ZoomInRequest, sizeof(BWStatus),
301     TwoPointsEngage, (XtPointer) BWDrawRectangle,
302     TwoPointsTerminate, (XtPointer) BWZoomIn,
303     NULL, (XtPointer) NULL},
304};
305
306static void ClassInitialize(void);
307static void Initialize(Widget wrequest, Widget wnew,
308		       ArgList argv, Cardinal *argc);
309static void Redisplay(Widget w, XEvent *event, Region region);
310static void Resize(Widget w);
311static void Destroy(Widget w);
312static void Refresh(BitmapWidget BW, Position x, Position y,
313		    Dimension width, Dimension height);
314static Boolean SetValues(Widget old, Widget request, Widget new,
315			 ArgList args, Cardinal *num_args);
316
317BitmapClassRec bitmapClassRec = {
318{   /* core fields */
319    /* superclass		*/	(WidgetClass) &simpleClassRec,
320    /* class_name		*/	"Bitmap",
321    /* widget_size		*/	sizeof(BitmapRec),
322    /* class_initialize		*/	ClassInitialize,
323    /* class_part_initialize	*/	NULL,
324    /* class_inited		*/	FALSE,
325    /* initialize		*/	Initialize,
326    /* initialize_hook		*/	NULL,
327    /* realize			*/	XtInheritRealize,
328    /* actions			*/	actions,
329    /* num_actions		*/	XtNumber(actions),
330    /* resources		*/	resources,
331    /* num_resources		*/	XtNumber(resources),
332    /* xrm_class		*/	NULLQUARK,
333    /* compress_motion		*/	TRUE,
334    /* compress_exposure	*/	FALSE,
335    /* compress_enterleave	*/	TRUE,
336    /* visible_interest		*/	TRUE,
337    /* destroy			*/	Destroy,
338    /* resize			*/	Resize,
339    /* expose			*/	Redisplay,
340    /* set_values		*/	SetValues,
341    /* set_values_hook		*/	NULL,
342    /* set_values_almost	*/	XtInheritSetValuesAlmost,
343    /* get_values_hook		*/	NULL,
344    /* accept_focus		*/	NULL,
345    /* version			*/	XtVersion,
346    /* callback_private		*/	NULL,
347    /* tm_table			*/	NULL , /* set in code */
348    /* query_geometry		*/	XtInheritQueryGeometry,
349    /* display_accelerator	*/	XtInheritDisplayAccelerator,
350    /* extension		*/	NULL,
351  },
352  {
353    /* empty			*/	XtInheritChangeSensitive,
354  },
355  {
356    /* targets                  */      targets,
357    /* num_trets                */      XtNumber(targets),
358    /* requests                 */      requests,
359    /* num_requests             */      XtNumber(requests),
360  }
361};
362
363WidgetClass bitmapWidgetClass = (WidgetClass) &bitmapClassRec;
364
365/* ARGSUSED */
366
367void
368BWDebug(Widget w, XEvent *event, String *params, Cardinal *num_params)
369{
370    DEBUG ^= True;
371}
372
373Pixmap
374BWGetPixmap(Widget w)
375{
376    BitmapWidget BW = (BitmapWidget) w;
377
378    return GetPixmap(BW, BW->bitmap.zoom.image);
379}
380
381Pixmap
382BWGetUnzoomedPixmap(Widget w)
383{
384    BitmapWidget BW = (BitmapWidget) w;
385    GC gc;
386    Pixmap pix;
387
388    if (BW->bitmap.zooming) {
389	pix = XCreatePixmap(XtDisplay(w), XtWindow(w),
390			    BW->bitmap.zoom.image->width,
391			    BW->bitmap.zoom.image->height, 1);
392	if (!(gc = XCreateGC(XtDisplay(w), pix,
393			     (unsigned long) 0, (XGCValues *) 0)))
394	    return (Pixmap) None;
395
396	XPutImage(XtDisplay(w), pix, gc,
397		  BW->bitmap.zoom.image,
398		  0, 0, 0, 0,
399		  BW->bitmap.zoom.image->width,
400		  BW->bitmap.zoom.image->height);
401	XPutImage(XtDisplay(w), pix, gc,
402		  BW->bitmap.image,
403		  0, 0,
404		  BW->bitmap.zoom.at_x,
405		  BW->bitmap.zoom.at_y,
406		  BW->bitmap.image->width,
407		  BW->bitmap.image->height);
408    }
409    else {
410	pix = XCreatePixmap(XtDisplay(w), XtWindow(w),
411			    BW->bitmap.image->width,
412			    BW->bitmap.image->height, 1);
413	if (! (gc = XCreateGC(XtDisplay(w), pix,
414			      (unsigned long) 0, (XGCValues *) 0)))
415	    return (Pixmap) None;
416
417	XPutImage(XtDisplay(w), pix, gc,
418		  BW->bitmap.image,
419		  0, 0, 0, 0,
420		  BW->bitmap.image->width,
421		  BW->bitmap.image->height);
422    }
423    XFreeGC(XtDisplay(w), gc);
424    return(pix);
425}
426
427
428XImage *
429GetImage(BitmapWidget BW, Pixmap pixmap)
430{
431    Window root;
432    int x, y;
433    unsigned int width, height, border_width, depth;
434    XImage *source, *image;
435
436    XGetGeometry(XtDisplay(BW), pixmap, &root, &x, &y,
437		 &width, &height, &border_width, &depth);
438
439    source = XGetImage(XtDisplay(BW), pixmap, x, y, width, height,
440		     1, XYPixmap);
441
442    image = ConvertToBitmapImage(BW, source);
443
444    return image;
445}
446
447XImage *
448CreateBitmapImage(BitmapWidget BW, char *data,
449		  Dimension width, Dimension height)
450{
451    XImage *image = XCreateImage(XtDisplay(BW),
452				 DefaultVisual(XtDisplay(BW),
453					       DefaultScreen(XtDisplay(BW))),
454				 1, XYBitmap, 0,
455				 data, width, height,
456				 8, ((int)width + 7) / 8);
457
458    image->height = height;
459    image->width = width;
460    image->depth = 1;
461    image->xoffset = 0;
462    image->format = XYBitmap;
463    image->data = (char *)data;
464    image->byte_order = LSBFirst;
465    image->bitmap_unit = 8;
466    image->bitmap_bit_order = LSBFirst;
467    image->bitmap_pad = 8;
468    image->bytes_per_line = ((int)width + 7) / 8;
469
470    return image;
471}
472
473void
474DestroyBitmapImage(XImage **image)
475{
476    /*XDestroyImage(*image);*/
477    if (image) {
478	if (*image) {
479	    if ((*image)->data)
480		XtFree((*image)->data);
481	    XtFree((char *)*image);
482	}
483	*image = NULL;
484    }
485}
486
487#if 0
488XImage *
489BWGetImage(Widget w, XEvent *event, String *params, Cardinal *num_params)
490{
491    BitmapWidget BW = (BitmapWidget) w;
492
493    return BW->bitmap.image;
494}
495#endif
496
497void
498BWChangeNotify(Widget w)
499{
500    BitmapWidget BW = (BitmapWidget) w;
501
502    if (BW->bitmap.notify)
503	(*BW->bitmap.notify)(w, NULL, NULL, NULL);
504}
505
506void
507BWNotify(Widget w, XtActionProc proc)
508{
509    BitmapWidget BW = (BitmapWidget) w;
510
511    BW->bitmap.notify = proc;
512}
513
514void
515BWSetChanged(Widget w)
516{
517    BitmapWidget BW = (BitmapWidget) w;
518
519    BW->bitmap.changed = True;
520}
521
522Boolean
523BWQueryChanged(Widget w)
524{
525    BitmapWidget BW = (BitmapWidget) w;
526
527    return BW->bitmap.changed;
528}
529
530void
531BWClearChanged(Widget w)
532{
533    BitmapWidget BW = (BitmapWidget) w;
534
535    BW->bitmap.changed = False;
536}
537
538Boolean
539BWQueryStored(Widget w)
540{
541    BitmapWidget BW = (BitmapWidget) w;
542
543    return (BW->bitmap.storage != NULL);
544}
545
546Boolean
547BWQueryStippled(Widget w)
548{
549    BitmapWidget BW = (BitmapWidget) w;
550
551    return BW->bitmap.stippled;
552}
553
554static void
555RedrawStippled(BitmapWidget BW)
556{
557  XExposeEvent event;
558
559  event.type = Expose;
560  event.display = XtDisplay((Widget)BW);
561  event.window = XtWindow((Widget)BW);
562  event.x = 0;
563  event.y = 0;
564  event.width = BW->core.width;
565  event.height = BW->core.height;
566  event.count = 0;
567
568  BWRedrawMark((Widget)BW);
569
570  BW->bitmap.stipple_change_expose_event = True;
571
572  XtDispatchEvent((XEvent *)&event);
573
574  BW->bitmap.stipple_change_expose_event = False;
575}
576
577void
578BWSwitchStippled(Widget w)
579{
580    BitmapWidget BW = (BitmapWidget) w;
581
582    RedrawStippled(BW);
583
584    BW->bitmap.stippled ^= True;
585    XSetFillStyle(XtDisplay(BW), BW->bitmap.highlighting_gc,
586		  (BW->bitmap.stippled ? FillStippled : FillSolid));
587
588    RedrawStippled(BW);
589}
590
591void
592BWSelect(Widget w, Position from_x, Position from_y,
593	 Position to_x, Position to_y, Time btime)
594{
595    BWMark(w, from_x, from_y, to_x, to_y);
596
597    BWGrabSelection(w, btime);
598}
599
600Boolean
601BWQueryAxes(Widget w)
602{
603    BitmapWidget BW = (BitmapWidget) w;
604
605    return BW->bitmap.axes;
606}
607
608void
609BWSwitchAxes(Widget w)
610{
611    BitmapWidget BW = (BitmapWidget) w;
612
613    BW->bitmap.axes ^= True;
614    BWHighlightAxes(w);
615}
616
617void
618BWAxes(Widget w, Boolean _switch)
619{
620    BitmapWidget BW = (BitmapWidget) w;
621
622    if (BW->bitmap.axes != _switch)
623	BWSwitchAxes(w);
624}
625
626void
627BWRedrawAxes(Widget w)
628{
629    BitmapWidget BW = (BitmapWidget) w;
630
631    if (BW->bitmap.axes)
632	BWHighlightAxes(w);
633}
634
635#if 0
636void
637BWPutImage(BitmapWidget w, Display *display, Drawable drawable, GC gc,
638	   Position x, Position y)
639{
640    BitmapWidget BW = (BitmapWidget) w;
641
642  XPutImage(display, drawable, gc, BW->bitmap.image,
643	    0, 0, x, y, BW->bitmap.image->width, BW->bitmap.image->height);
644}
645#endif
646
647static String
648StripFilename(String filename)
649{
650    char *begin = strrchr(filename, '/');
651    char *end, *result;
652    int length;
653
654    if (filename) {
655	begin = (begin ? begin + 1 : filename);
656	end = strchr(begin, '.'); /* change to strrchr to allow longer names */
657	length = (end ? (end - begin) : strlen (begin));
658	result = (char *) XtMalloc (length + 1);
659	strncpy (result, begin, length);
660	result [length] = '\0';
661	return (result);
662    }
663    else
664	return (NULL);
665}
666
667static int
668XmuWriteBitmapDataToFile(String filename, String basename,
669			 int width, int height, char *datap,
670			 int x_hot, int y_hot)
671{
672    FILE *file;
673    int i, data_length;
674
675    data_length = Length(width, height);
676
677    if(!filename || !strcmp(filename, "") || !strcmp(filename, "-")) {
678	file = stdout;
679	filename = "dummy";
680	}
681    else
682    	file = fopen(filename, "w+");
683
684    if (!basename || !strcmp(basename, "") || !strcmp(basename, "-"))
685	basename = StripFilename(filename);
686
687    if (file) {
688	fprintf(file, "#define %s_width %d\n", basename, width);
689	fprintf(file, "#define %s_height %d\n", basename, height);
690	if (QuerySet(x_hot, y_hot)) {
691	    fprintf(file, "#define %s_x_hot %d\n", basename, x_hot);
692	    fprintf(file, "#define %s_y_hot %d\n", basename, y_hot);
693	}
694	fprintf(file, "static unsigned char %s_bits[] = {\n   0x%02x",
695		basename, (unsigned char) datap[0]);
696	for(i = 1; i < data_length; i++) {
697	    fprintf(file, ",");
698	    fprintf(file, (i % 12) ? " " : "\n   ");
699	    fprintf(file, "0x%02x", (unsigned char) datap[i]);
700	}
701	fprintf(file, "};\n");
702
703	if (file != stdout)
704	    fclose(file);
705
706	return BitmapSuccess;
707    }
708
709    return 1;
710}
711
712/*
713 *
714 */
715
716				/* ARGSUSED */
717static void
718CvtStringToButtonFunction(XrmValuePtr args, /* not used */
719			  Cardinal *num_args, /* not used */
720			  XrmValuePtr from_val,
721			  XrmValuePtr to_val)
722{
723  static int button_function;
724  char lower_name[80];
725
726  XmuCopyISOLatin1Lowered (lower_name, (char*)from_val->addr);
727
728  if (!strcmp(lower_name, XtClear)) {
729    button_function = Clear;
730    to_val->addr = (XPointer) &button_function;
731    to_val->size = sizeof(button_function);
732    return;
733  }
734
735  if (!strcmp(lower_name, XtSet)) {
736    button_function = Set;
737    to_val->addr = (XPointer) &button_function;
738    to_val->size = sizeof(button_function);
739    return;
740  }
741
742  if (!strcmp(lower_name, XtInvert)) {
743    button_function = Invert;
744    to_val->addr = (XPointer) &button_function;
745    to_val->size = sizeof(button_function);
746    return;
747  }
748
749  XtStringConversionWarning(from_val->addr, XtRButtonFunction);
750  button_function = Clear;
751  to_val->addr = (XPointer) &button_function;
752  to_val->size = sizeof(button_function);
753
754}
755
756static void
757ClassInitialize(void)
758{
759  char *tm_table = XtMalloc(strlen(translations1) + strlen(translations2) + 1);
760  strcpy(tm_table, translations1);
761  strcat(tm_table, translations2);
762  bitmapClassRec.core_class.tm_table = tm_table;
763
764  XawInitializeWidgetSet();
765  XtAddConverter(XtRString, XtRButtonFunction, CvtStringToButtonFunction,
766		 NULL, 0);
767  DEBUG = False;
768}
769
770static void
771SetSizeFromSizeResource(BitmapWidget bw)
772{
773  if (BWParseSize(bw->bitmap.size,
774		  &bw->bitmap.width,
775		  &bw->bitmap.height)
776      ==
777      False) {
778    bw->bitmap.width = FallbackBitmapWidth;
779    bw->bitmap.height = FallbackBitmapHeight;
780    XtWarning("Cannot parse the size resource.  BitmapWidget");
781  }
782}
783
784
785/* ARGSUSED */
786static void
787Initialize(Widget wrequest, Widget wnew, ArgList argv, Cardinal *argc)
788{
789    BitmapWidget new = (BitmapWidget) wnew;
790
791    XGCValues  values;
792    XtGCMask   mask;
793    char *image_data, *buffer_data;
794
795    new->bitmap.stipple_change_expose_event = False;
796    new->bitmap.notify = NULL;
797    new->bitmap.cardinal = 0;
798    new->bitmap.current = 0;
799    new->bitmap.fold = False;
800    new->bitmap.changed = False;
801    new->bitmap.zooming = False;
802    new->bitmap.selection.own = False;
803    new->bitmap.selection.limbo = False;
804
805    new->bitmap.request_stack = (BWRequestStack *)
806	XtMalloc(sizeof(BWRequestStack));
807
808    new->bitmap.request_stack[0].request = NULL;
809    new->bitmap.request_stack[0].call_data = NULL;
810    new->bitmap.request_stack[0].trap = False;
811
812    SetSizeFromSizeResource(new);
813
814    new->core.width = new->bitmap.width * new->bitmap.squareW +
815	2 * new->bitmap.margin;
816    new->core.height = new->bitmap.height * new->bitmap.squareH +
817	2 * new->bitmap.margin;
818
819    new->bitmap.hot.x = new->bitmap.hot.y = NotSet;
820    new->bitmap.buffer_hot.x = new->bitmap.buffer_hot.y = NotSet;
821
822    new->bitmap.mark.from_x = new->bitmap.mark.from_y = NotSet;
823    new->bitmap.mark.to_x = new->bitmap.mark.to_y = NotSet;
824    new->bitmap.buffer_mark.from_x = new->bitmap.buffer_mark.from_y = NotSet;
825    new->bitmap.buffer_mark.to_x = new->bitmap.buffer_mark.to_y = NotSet;
826
827    values.foreground = new->bitmap.foreground_pixel;
828    values.background = new->core.background_pixel;
829    values.foreground ^= values.background;
830    values.function = GXxor;
831    mask = GCForeground | GCBackground | GCFunction;
832    new->bitmap.drawing_gc = XCreateGC(XtDisplay(new),
833				       RootWindow(XtDisplay(new),
834				       DefaultScreen(XtDisplay(new))),
835				       mask, &values);
836
837    values.foreground = new->bitmap.highlight_pixel;
838    values.background = new->core.background_pixel;
839    values.foreground ^= values.background;
840    values.function = GXxor;
841    mask = GCForeground | GCBackground | GCFunction;
842    if (new->bitmap.stipple != XtUnspecifiedPixmap)
843    {
844	values.stipple = new->bitmap.stipple;
845	mask |= GCStipple | GCFillStyle;
846    }
847    values.fill_style = (new->bitmap.stippled ? FillStippled : FillSolid);
848
849    new->bitmap.highlighting_gc = XCreateGC(XtDisplay(new),
850					    RootWindow(XtDisplay(new),
851					       DefaultScreen(XtDisplay(new))),
852					    mask, &values);
853
854
855    values.foreground = new->bitmap.frame_pixel;
856    values.background = new->core.background_pixel;
857    values.foreground ^= values.background;
858    mask = GCForeground | GCBackground | GCFunction;
859    if (new->bitmap.dashes != XtUnspecifiedPixmap)
860    {
861	values.stipple = new->bitmap.dashes;
862	mask |= GCStipple | GCFillStyle;
863    }
864    values.fill_style = (new->bitmap.dashed ? FillStippled : FillSolid);
865
866    new->bitmap.frame_gc = XCreateGC(XtDisplay(new),
867				     RootWindow(XtDisplay(new),
868						DefaultScreen(XtDisplay(new))),
869				     mask, &values);
870
871    values.foreground = new->bitmap.highlight_pixel;
872    values.background = new->core.background_pixel;
873    values.foreground ^= values.background;
874    mask = GCForeground | GCBackground | GCFunction;
875    new->bitmap.axes_gc = XCreateGC(XtDisplay(new),
876				     RootWindow(XtDisplay(new),
877						DefaultScreen(XtDisplay(new))),
878				     mask, &values);
879
880    image_data = CreateCleanData(Length(new->bitmap.width,
881					new->bitmap.height));
882    buffer_data = CreateCleanData(Length(new->bitmap.width,
883					 new->bitmap.height));
884
885    new->bitmap.storage = NULL;
886
887    new->bitmap.image = CreateBitmapImage(new,
888					  image_data,
889					  new->bitmap.width,
890					  new->bitmap.height);
891    new->bitmap.buffer = CreateBitmapImage(new,
892					   buffer_data,
893					   new->bitmap.width,
894					   new->bitmap.height);
895
896    /* Read file */
897    {
898	int status;
899	XImage *image, *buffer;
900	unsigned char *image_data;
901	char *buffer_data;
902	unsigned int width, height;
903	int x_hot, y_hot;
904
905	status = XmuReadBitmapDataFromFile(new->bitmap.filename,
906					   &width, &height, &image_data,
907					   &x_hot, &y_hot);
908	if (status == BitmapSuccess) {
909
910	    buffer_data = CreateCleanData(Length(width, height));
911
912	    image = CreateBitmapImage(new, (char *)image_data, width, height);
913	    buffer = CreateBitmapImage(new, buffer_data, width, height);
914
915	    TransferImageData(new->bitmap.image, buffer);
916
917	    DestroyBitmapImage(&new->bitmap.image);
918	    DestroyBitmapImage(&new->bitmap.buffer);
919
920	    new->bitmap.image = image;
921	    new->bitmap.buffer = buffer;
922	    new->bitmap.width = width;
923	    new->bitmap.height = height;
924
925	    new->bitmap.hot.x = x_hot;
926	    new->bitmap.hot.y = y_hot;
927
928	    new->bitmap.changed = False;
929	    new->bitmap.zooming = False;
930	}
931
932	new->bitmap.filename = XtNewString(new->bitmap.filename);
933
934	if (!strcmp(new->bitmap.basename, "")) {
935	    new->bitmap.basename = StripFilename(new->bitmap.filename);
936	}
937	else
938	  new->bitmap.basename = XtNewString(new->bitmap.basename);
939    }
940
941    Resize((Widget)new);
942}
943
944
945/* returns False if the format is wrong */
946Boolean
947BWParseSize(String size, Dimension *width, Dimension *height)
948{
949  int x, y;
950  unsigned int w, h;
951  int status;
952
953  status = XParseGeometry(size, &x, &y, &w, &h);
954
955  if (status & (WidthValue | HeightValue)) {
956    *width = (Dimension) w;
957    *height = (Dimension) h;
958    return True;
959  }
960  else return False;
961
962}
963
964
965Boolean
966BWQueryMarked(Widget w)
967{
968    BitmapWidget BW = (BitmapWidget) w;
969
970    return QuerySet(BW->bitmap.mark.from_x, BW->bitmap.mark.from_y);
971}
972
973static void
974FixMark(BitmapWidget BW)
975{
976    if (QuerySet(BW->bitmap.mark.from_x, BW->bitmap.mark.from_y)) {
977	BW->bitmap.mark.from_x = min(BW->bitmap.mark.from_x,
978				     BW->bitmap.image->width);
979	BW->bitmap.mark.from_y = min(BW->bitmap.mark.from_y,
980				     BW->bitmap.image->height);
981	BW->bitmap.mark.to_x = min(BW->bitmap.mark.to_x,
982				   BW->bitmap.image->width);
983	BW->bitmap.mark.to_y = min(BW->bitmap.mark.to_y,
984				   BW->bitmap.image->height);
985
986	if((BW->bitmap.mark.from_x == BW->bitmap.mark.from_y) &&
987	   (BW->bitmap.mark.to_x   == BW->bitmap.mark.to_y))
988	    BW->bitmap.mark.from_x =
989		BW->bitmap.mark.from_y =
990		    BW->bitmap.mark.to_x =
991			BW->bitmap.mark.to_y = NotSet;
992    }
993}
994
995/* ARGSUSED */
996int
997BWStoreFile(Widget w, String filename, String *basename)
998{
999    BitmapWidget BW = (BitmapWidget) w;
1000    int status;
1001    unsigned char *storage_data;
1002    unsigned int width, height;
1003    int x_hot, y_hot;
1004
1005    status = XmuReadBitmapDataFromFile(filename, &width, &height,
1006				       &storage_data, &x_hot, &y_hot);
1007    if (status == BitmapSuccess) {
1008
1009	DestroyBitmapImage(&BW->bitmap.storage);
1010
1011	BW->bitmap.storage = CreateBitmapImage(BW, (char *)storage_data, width, height);
1012
1013	return BitmapSuccess;
1014    }
1015    else
1016	XtWarning(" read file failed.  BitmapWidget");
1017
1018    return status;
1019}
1020
1021String
1022BWUnparseStatus(Widget w)
1023{
1024    BitmapWidget BW = (BitmapWidget) w;
1025
1026    XmuSnprintf(BW->bitmap.status, sizeof(BW->bitmap.status),
1027	    "Filename: %s  Basename: %s  Size: %dx%d",
1028	    (strcmp(BW->bitmap.filename, "") ? BW->bitmap.filename : "<none>"),
1029	    (strcmp(BW->bitmap.basename, "") ? BW->bitmap.basename : "<none>"),
1030	    BW->bitmap.width, BW->bitmap.height);
1031
1032    return BW->bitmap.status;
1033}
1034
1035void
1036BWChangeFilename(Widget w, String str)
1037{
1038  BitmapWidget BW = (BitmapWidget) w;
1039
1040  if (str) {
1041    XtFree(BW->bitmap.filename);
1042    BW->bitmap.filename = XtNewString( str);
1043  }
1044}
1045
1046void
1047BWChangeBasename(Widget w, String str)
1048{
1049  BitmapWidget BW = (BitmapWidget) w;
1050
1051  if (str) {
1052    XtFree(BW->bitmap.basename);
1053    BW->bitmap.basename = XtNewString(str);
1054  }
1055}
1056
1057
1058int
1059BWReadFile(Widget w, String filename, String basename) /* ARGSUSED */
1060{
1061    BitmapWidget BW = (BitmapWidget) w;
1062    int status;
1063    XImage *image, *buffer;
1064    unsigned char *image_data;
1065    char *buffer_data;
1066    unsigned int width, height;
1067    int x_hot, y_hot;
1068
1069    if (!filename)
1070	filename = BW->bitmap.filename;
1071
1072    status = XmuReadBitmapDataFromFile(filename, &width, &height, &image_data,
1073				       &x_hot, &y_hot);
1074    if (status == BitmapSuccess) {
1075
1076	buffer_data = CreateCleanData(Length(width, height));
1077
1078	image = CreateBitmapImage(BW, (char *)image_data, width, height);
1079	buffer = CreateBitmapImage(BW, buffer_data, width, height);
1080
1081	TransferImageData(BW->bitmap.image, buffer);
1082
1083	DestroyBitmapImage(&BW->bitmap.image);
1084	DestroyBitmapImage(&BW->bitmap.buffer);
1085
1086	BW->bitmap.image = image;
1087	BW->bitmap.buffer = buffer;
1088	BW->bitmap.width = width;
1089	BW->bitmap.height = height;
1090
1091	BW->bitmap.hot.x = x_hot;
1092	BW->bitmap.hot.y = y_hot;
1093
1094	BW->bitmap.changed = False;
1095	BW->bitmap.zooming = False;
1096
1097	XtFree(BW->bitmap.filename);
1098	BW->bitmap.filename = XtNewString(filename);
1099	XtFree(BW->bitmap.basename);
1100	BW->bitmap.basename= XtNewString(StripFilename(filename));
1101
1102	BWUnmark(w);
1103
1104	Resize((Widget)BW);
1105
1106	if (BW->core.visible) {
1107	    XClearArea(XtDisplay(BW), XtWindow(BW),
1108		       0, 0,
1109		       BW->core.width, BW->core.height,
1110		       True);
1111	}
1112
1113	return BitmapSuccess;
1114    }
1115    else
1116	XtWarning(" read file failed.  BitmapWidget");
1117
1118    return status;
1119}
1120
1121#if 0
1122void
1123BWSetImage(Widget w, XImage *image)
1124{
1125    BitmapWidget BW = (BitmapWidget) w;
1126    XImage *buffer;
1127    char *buffer_data;
1128
1129    buffer_data = CreateCleanData(Length(image->width, image->height));
1130    buffer = CreateBitmapImage(BW, buffer_data,
1131			       (Dimension) image->width,
1132			       (Dimension) image->height);
1133
1134    TransferImageData(BW->bitmap.image, buffer);
1135
1136    DestroyBitmapImage(&BW->bitmap.image);
1137    DestroyBitmapImage(&BW->bitmap.buffer);
1138
1139    BW->bitmap.image = image;
1140    BW->bitmap.buffer = buffer;
1141    BW->bitmap.width = image->width;
1142    BW->bitmap.height = image->height;
1143
1144    Resize((Widget)BW);
1145
1146    if (BW->core.visible) {
1147	XClearArea(XtDisplay(BW), XtWindow(BW),
1148		   0, 0,
1149		   BW->core.width, BW->core.height,
1150		   True);
1151    }
1152}
1153#endif
1154
1155int
1156BWWriteFile(Widget w, String filename, String basename)
1157{
1158    BitmapWidget BW = (BitmapWidget) w;
1159    char *data;
1160    XImage *image;
1161    XPoint hot;
1162    int status;
1163
1164    if (BW->bitmap.zooming) {
1165        data = XtMalloc(Length(BW->bitmap.zoom.image->width,
1166			       BW->bitmap.zoom.image->height));
1167	memmove( data, BW->bitmap.zoom.image->data,
1168	      Length(BW->bitmap.zoom.image->width,
1169		     BW->bitmap.zoom.image->height));
1170	image = CreateBitmapImage(BW, data,
1171				  (Dimension) BW->bitmap.zoom.image->width,
1172				  (Dimension) BW->bitmap.zoom.image->height);
1173	CopyImageData(BW->bitmap.image, image,
1174		      0, 0,
1175		      BW->bitmap.image->width - 1,
1176		      BW->bitmap.image->height - 1,
1177		      BW->bitmap.zoom.at_x, BW->bitmap.zoom.at_y);
1178
1179	if (QuerySet(BW->bitmap.hot.x, BW->bitmap.hot.y)) {
1180	    hot.x = BW->bitmap.hot.x + BW->bitmap.zoom.at_x;
1181	    hot.y = BW->bitmap.hot.y + BW->bitmap.zoom.at_y;
1182	}
1183	else
1184	    hot = BW->bitmap.zoom.hot;
1185    }
1186    else {
1187	image = BW->bitmap.image;
1188	hot = BW->bitmap.hot;
1189    }
1190
1191    if (!filename) filename = BW->bitmap.filename;
1192    else {
1193	XtFree(BW->bitmap.filename);
1194	BW->bitmap.filename = XtNewString(filename);
1195	XtFree(BW->bitmap.basename);
1196	BW->bitmap.basename= XtNewString(StripFilename(filename));
1197    }
1198    if (!basename) basename = BW->bitmap.basename;
1199    else {
1200	XtFree(BW->bitmap.basename);
1201	BW->bitmap.basename = XtNewString(basename);
1202    }
1203
1204    if (DEBUG)
1205	fprintf(stderr, "Saving filename: %s %s\n", filename, basename);
1206
1207    status = XmuWriteBitmapDataToFile(filename, basename,
1208				      image->width, image->height, image->data,
1209				      hot.x, hot.y);
1210    if (BW->bitmap.zooming)
1211	DestroyBitmapImage(&image);
1212
1213    if (status == BitmapSuccess)
1214	BW->bitmap.changed = False;
1215
1216    return status;
1217}
1218
1219String
1220BWGetFilename(Widget w, String *str)
1221{
1222    BitmapWidget BW = (BitmapWidget) w;
1223
1224    *str = XtNewString(BW->bitmap.filename);
1225
1226    return *str;
1227}
1228
1229String
1230BWGetFilepath(Widget w, String *str)
1231{
1232    BitmapWidget BW = (BitmapWidget) w;
1233    String end;
1234
1235    *str = XtNewString(BW->bitmap.filename);
1236    end = strrchr(*str, '/');
1237
1238    if (end)
1239	*(end + 1) = '\0';
1240    else
1241	**str = '\0';
1242
1243    return *str;
1244}
1245
1246
1247String
1248BWGetBasename(Widget w, String *str)
1249{
1250    BitmapWidget BW = (BitmapWidget) w;
1251
1252    *str = XtNewString(BW->bitmap.basename);
1253
1254    return *str;
1255}
1256
1257static void
1258FixHotSpot(BitmapWidget BW)
1259{
1260    if (!QueryInBitmap(BW, BW->bitmap.hot.x, BW->bitmap.hot.y))
1261	BW->bitmap.hot.x = BW->bitmap.hot.y = NotSet;
1262}
1263
1264static void
1265ZoomOut(BitmapWidget BW)
1266{
1267    CopyImageData(BW->bitmap.image, BW->bitmap.zoom.image,
1268		  0, 0,
1269		  BW->bitmap.image->width - 1,
1270		  BW->bitmap.image->height - 1,
1271		  BW->bitmap.zoom.at_x, BW->bitmap.zoom.at_y);
1272
1273    DestroyBitmapImage(&BW->bitmap.image);
1274    DestroyBitmapImage(&BW->bitmap.buffer);
1275
1276    BW->bitmap.image = BW->bitmap.zoom.image;
1277    BW->bitmap.buffer = BW->bitmap.zoom.buffer;
1278    BW->bitmap.width = BW->bitmap.image->width;
1279    BW->bitmap.height = BW->bitmap.image->height;
1280    BW->bitmap.fold = BW->bitmap.zoom.fold;
1281    BW->bitmap.changed |= BW->bitmap.zoom.changed;
1282    BW->bitmap.grid = BW->bitmap.zoom.grid;
1283
1284    if (QuerySet(BW->bitmap.hot.x, BW->bitmap.hot.y)) {
1285	BW->bitmap.hot.x += BW->bitmap.zoom.at_x;
1286	BW->bitmap.hot.y += BW->bitmap.zoom.at_y;
1287    }
1288    else
1289	BW->bitmap.hot = BW->bitmap.zoom.hot;
1290
1291    BW->bitmap.mark.from_x = NotSet;
1292    BW->bitmap.mark.from_y = NotSet;
1293    BW->bitmap.mark.to_x = NotSet;
1294    BW->bitmap.mark.to_y = NotSet;
1295    BW->bitmap.zooming = False;
1296}
1297
1298void
1299BWZoomOut(Widget w)
1300{
1301    BitmapWidget BW = (BitmapWidget) w;
1302
1303    if (BW->bitmap.zooming) {
1304	ZoomOut(BW);
1305
1306	Resize((Widget)BW);
1307	if (BW->core.visible)
1308	    XClearArea(XtDisplay(BW), XtWindow(BW),
1309		       0, 0,
1310		       BW->core.width, BW->core.height,
1311		       True);
1312    }
1313}
1314
1315
1316void
1317BWZoomMarked(Widget w)
1318{
1319    BitmapWidget BW = (BitmapWidget) w;
1320
1321    BWZoomIn(w,
1322	     BW->bitmap.mark.from_x, BW->bitmap.mark.from_y,
1323	     BW->bitmap.mark.to_x,   BW->bitmap.mark.to_y);
1324}
1325
1326void
1327BWZoomIn(Widget w,
1328	 Position from_x, Position from_y,
1329	 Position to_x, Position to_y)
1330{
1331    BitmapWidget BW = (BitmapWidget) w;
1332    XImage *image, *buffer;
1333    Dimension width, height;
1334    char *image_data, *buffer_data;
1335
1336    if (BW->bitmap.zooming)
1337	ZoomOut(BW);
1338
1339    QuerySwap(from_x, to_x);
1340    QuerySwap(from_y, to_y);
1341    from_x = max(0, from_x);
1342    from_y = max(0, from_y);
1343    to_x = min(BW->bitmap.width - 1, to_x);
1344    to_y = min(BW->bitmap.height - 1, to_y);
1345
1346    width = to_x - from_x + 1;
1347    height = to_y - from_y + 1;
1348
1349    image_data = CreateCleanData(Length(width, height));
1350    buffer_data = CreateCleanData(Length(width, height));
1351
1352    image = CreateBitmapImage(BW, image_data, width, height);
1353    buffer = CreateBitmapImage(BW, buffer_data, width, height);
1354
1355    CopyImageData(BW->bitmap.image, image, from_x, from_y, to_x, to_y, 0, 0);
1356    CopyImageData(BW->bitmap.buffer, buffer, from_x, from_y, to_x, to_y, 0, 0);
1357
1358    BW->bitmap.zoom.image = BW->bitmap.image;
1359    BW->bitmap.zoom.buffer = BW->bitmap.buffer;
1360    BW->bitmap.zoom.at_x = from_x;
1361    BW->bitmap.zoom.at_y = from_y;
1362    BW->bitmap.zoom.fold = BW->bitmap.fold;
1363    BW->bitmap.zoom.changed = BW->bitmap.changed;
1364    BW->bitmap.zoom.hot = BW->bitmap.hot;
1365    BW->bitmap.zoom.grid = BW->bitmap.grid;
1366
1367    BW->bitmap.image = image;
1368    BW->bitmap.buffer = buffer;
1369    BW->bitmap.width = width;
1370    BW->bitmap.height = height;
1371    BW->bitmap.changed = False;
1372    BW->bitmap.hot.x -= from_x;
1373    BW->bitmap.hot.y -= from_y;
1374    BW->bitmap.mark.from_x = NotSet;
1375    BW->bitmap.mark.from_y = NotSet;
1376    BW->bitmap.mark.to_x = NotSet;
1377    BW->bitmap.mark.to_y = NotSet;
1378    BW->bitmap.zooming = True;
1379    BW->bitmap.grid = True; /* potencially true, could use a resource here */
1380
1381    FixHotSpot(BW);
1382
1383    Resize((Widget)BW);
1384    if (BW->core.visible)
1385	XClearArea(XtDisplay(BW), XtWindow(BW),
1386		   0, 0,
1387		   BW->core.width, BW->core.height,
1388		   True);
1389}
1390
1391
1392void
1393BWRescale(Widget w, Dimension width, Dimension height)
1394{
1395    BitmapWidget BW = (BitmapWidget) w;
1396    XImage *image, *buffer;
1397    char *buffer_data;
1398
1399    if (BW->bitmap.zooming)
1400	ZoomOut(BW);
1401
1402    image = ScaleBitmapImage(BW, BW->bitmap.image,
1403		       (double) width / (double) BW->bitmap.image->width,
1404		       (double) height / (double) BW->bitmap.image->height);
1405
1406    buffer_data = CreateCleanData(Length(image->width, image->height));
1407    buffer = CreateBitmapImage(BW, buffer_data,
1408			       (Dimension) image->width,
1409			       (Dimension) image->height);
1410
1411    TransferImageData(BW->bitmap.buffer, buffer);
1412
1413    DestroyBitmapImage(&BW->bitmap.image);
1414    DestroyBitmapImage(&BW->bitmap.buffer);
1415
1416    BW->bitmap.image = image;
1417    BW->bitmap.buffer = buffer;
1418    BW->bitmap.width = image->width;
1419    BW->bitmap.height = image->height;
1420
1421    FixHotSpot(BW);
1422    FixMark(BW);
1423
1424    Resize((Widget)BW);
1425    if (BW->core.visible)
1426	XClearArea(XtDisplay(BW), XtWindow(BW),
1427		   0, 0,
1428		   BW->core.width, BW->core.height,
1429		   True);
1430}
1431
1432Boolean
1433BWQueryZooming(Widget w)
1434{
1435    BitmapWidget BW = (BitmapWidget) w;
1436
1437    return BW->bitmap.zooming;
1438}
1439
1440
1441static void
1442ResizeGrid(BitmapWidget BW, Dimension width, Dimension height)
1443{
1444  XImage *image, *buffer;
1445  char *image_data, *buffer_data;
1446
1447  if (BW->bitmap.zooming)
1448    ZoomOut(BW);
1449
1450  image_data = CreateCleanData(Length(width, height));
1451  buffer_data = CreateCleanData(Length(width, height));
1452
1453  image = CreateBitmapImage(BW, image_data, width, height);
1454  buffer = CreateBitmapImage(BW, buffer_data, width, height);
1455
1456  TransferImageData(BW->bitmap.image, image);
1457  TransferImageData(BW->bitmap.buffer, buffer);
1458
1459  DestroyBitmapImage(&BW->bitmap.image);
1460  DestroyBitmapImage(&BW->bitmap.buffer);
1461
1462  BW->bitmap.image = image;
1463  BW->bitmap.buffer = buffer;
1464  BW->bitmap.width = width;
1465  BW->bitmap.height = height;
1466
1467  FixHotSpot(BW);
1468  FixMark(BW);
1469}
1470
1471void
1472BWResize(Widget w, Dimension width, Dimension height)
1473{
1474    BitmapWidget BW = (BitmapWidget) w;
1475
1476    ResizeGrid(BW, width, height);
1477
1478    Resize((Widget)BW);
1479    if (BW->core.visible)
1480	XClearArea(XtDisplay(BW), XtWindow(BW),
1481		   0, 0,
1482		   BW->core.width, BW->core.height,
1483		   True);
1484}
1485
1486static void
1487Destroy(Widget w)
1488{
1489    BitmapWidget BW = (BitmapWidget) w;
1490
1491    XFreeGC(XtDisplay(w), BW->bitmap.drawing_gc);
1492    XFreeGC(XtDisplay(w), BW->bitmap.highlighting_gc);
1493    XFreeGC(XtDisplay(w), BW->bitmap.frame_gc);
1494    XFreeGC(XtDisplay(w), BW->bitmap.axes_gc);
1495    BWRemoveAllRequests(w);
1496
1497    XtFree(BW->bitmap.filename);
1498    XtFree(BW->bitmap.basename);
1499}
1500
1501
1502static void
1503Resize(Widget w)
1504{
1505    BitmapWidget BW = (BitmapWidget) w;
1506
1507    Dimension squareW, squareH;
1508
1509    squareW = max(1, ((int)BW->core.width - 2 * (int)BW->bitmap.margin) /
1510		  (int)BW->bitmap.width);
1511    squareH = max(1, ((int)BW->core.height - 2 * (int)BW->bitmap.margin) /
1512		  (int)BW->bitmap.height);
1513
1514    if (BW->bitmap.proportional)
1515	BW->bitmap.squareW = BW->bitmap.squareH = min(squareW, squareH);
1516    else {
1517	BW->bitmap.squareW = squareW;
1518	BW->bitmap.squareH = squareH;
1519    }
1520
1521    BW->bitmap.horizOffset = max((Position)BW->bitmap.margin,
1522				 (Position)(BW->core.width -
1523					    BW->bitmap.width *
1524					    BW->bitmap.squareW) / 2);
1525    BW->bitmap.vertOffset = max((Position)BW->bitmap.margin,
1526				(Position)(BW->core.height -
1527					   BW->bitmap.height *
1528					   BW->bitmap.squareH) / 2);
1529
1530    BW->bitmap.grid &= ((BW->bitmap.squareW > BW->bitmap.grid_tolerance) &&
1531			(BW->bitmap.squareH > BW->bitmap.grid_tolerance));
1532}
1533
1534/* ARGSUSED */
1535static void
1536Redisplay(Widget w, XEvent *event, Region region)
1537{
1538     BitmapWidget BW = (BitmapWidget) w;
1539
1540  if(event->type == Expose
1541     &&
1542     BW->core.visible)
1543    if (BW->bitmap.stipple_change_expose_event == False)
1544      Refresh(BW,
1545	      event->xexpose.x, event->xexpose.y,
1546	      event->xexpose.width, event->xexpose.height);
1547}
1548
1549void
1550BWClip(Widget w, Position x, Position y, Dimension width, Dimension height)
1551{
1552    Position      from_x, from_y,
1553                  to_x, to_y;
1554    BitmapWidget BW = (BitmapWidget) w;
1555    XRectangle rectangle;
1556
1557    from_x = InBitmapX(BW, x);
1558    from_y = InBitmapY(BW, y);
1559    to_x = InBitmapX(BW, x + width);
1560    to_y = InBitmapY(BW, y + height);
1561    QuerySwap(from_x, to_x);
1562    QuerySwap(from_y, to_y);
1563    from_x = max(0, from_x);
1564    from_y = max(0, from_y);
1565    to_x = min(BW->bitmap.width - 1, to_x);
1566    to_y = min(BW->bitmap.height - 1, to_y);
1567
1568    rectangle.x = InWindowX(BW, from_x);
1569    rectangle.y = InWindowY(BW, from_y);
1570    rectangle.width = InWindowX(BW, to_x  + 1) - InWindowX(BW, from_x);
1571    rectangle.height = InWindowY(BW, to_y + 1) - InWindowY(BW, from_y);
1572    XSetClipRectangles(XtDisplay(BW),
1573		       BW->bitmap.highlighting_gc,
1574		       0, 0,
1575		       &rectangle, 1,
1576		       Unsorted);
1577    XSetClipRectangles(XtDisplay(BW),
1578		       BW->bitmap.drawing_gc,
1579		       0, 0,
1580		       &rectangle, 1,
1581		       Unsorted);
1582    XSetClipRectangles(XtDisplay(BW),
1583		       BW->bitmap.frame_gc,
1584		       0, 0,
1585		       &rectangle, 1,
1586		       Unsorted);
1587    XSetClipRectangles(XtDisplay(BW),
1588		       BW->bitmap.axes_gc,
1589		       0, 0,
1590		       &rectangle, 1,
1591		       Unsorted);
1592}
1593
1594void
1595BWUnclip(Widget w)
1596{
1597    BitmapWidget BW = (BitmapWidget) w;
1598    XRectangle rectangle;
1599
1600    rectangle.x = InWindowX(BW, 0);
1601    rectangle.y = InWindowY(BW, 0);
1602    rectangle.width = InWindowX(BW, BW->bitmap.width) - InWindowX(BW, 0);
1603    rectangle.height = InWindowY(BW, BW->bitmap.height) - InWindowY(BW, 0);
1604    XSetClipRectangles(XtDisplay(BW),
1605		       BW->bitmap.highlighting_gc,
1606		       0, 0,
1607		       &rectangle, 1,
1608		       Unsorted);
1609    XSetClipRectangles(XtDisplay(BW),
1610		       BW->bitmap.drawing_gc,
1611		       0, 0,
1612		       &rectangle, 1,
1613		       Unsorted);
1614    XSetClipRectangles(XtDisplay(BW),
1615		       BW->bitmap.frame_gc,
1616		       0, 0,
1617		       &rectangle, 1,
1618		       Unsorted);
1619    XSetClipRectangles(XtDisplay(BW),
1620		       BW->bitmap.axes_gc,
1621		       0, 0,
1622		       &rectangle, 1,
1623		       Unsorted);
1624}
1625
1626static void
1627Refresh(BitmapWidget BW, Position x, Position y,
1628	Dimension width, Dimension height)
1629{
1630    XRectangle rectangle;
1631
1632    rectangle.x = min(x, InWindowX(BW, InBitmapX(BW, x)));
1633    rectangle.y = min(y, InWindowY(BW, InBitmapY(BW, y)));
1634    rectangle.width = max(x + width,
1635		     InWindowX(BW, InBitmapX(BW, x + width)+1)) - rectangle.x;
1636    rectangle.height = max(y + height,
1637		     InWindowY(BW, InBitmapY(BW, y + height)+1)) - rectangle.y;
1638
1639    XClearArea(XtDisplay(BW), XtWindow(BW),
1640	       rectangle.x, rectangle.y,
1641	       rectangle.width, rectangle.height,
1642	       False);
1643
1644    XSetClipRectangles(XtDisplay(BW),
1645		       BW->bitmap.frame_gc,
1646		       0, 0,
1647		       &rectangle, 1,
1648		       Unsorted);
1649
1650    XDrawRectangle(XtDisplay(BW), XtWindow(BW),
1651		   BW->bitmap.frame_gc,
1652		   InWindowX(BW, 0) - 1, InWindowY(BW, 0) - 1,
1653		   InWindowX(BW, BW->bitmap.width) - InWindowX(BW, 0) + 1,
1654		   InWindowY(BW, BW->bitmap.height) - InWindowY(BW, 0) + 1);
1655
1656    BWClip((Widget) BW, x, y, width, height);
1657
1658    BWRedrawGrid((Widget) BW, x, y, width, height);
1659
1660    BWRedrawSquares((Widget) BW, x, y, width, height);
1661
1662    BWRedrawMark((Widget) BW);
1663    BWRedrawHotSpot((Widget) BW);
1664    BWRedrawAxes((Widget) BW);
1665    BWUnclip((Widget) BW);
1666}
1667
1668Boolean
1669BWQueryGrid(Widget w)
1670{
1671    BitmapWidget BW = (BitmapWidget) w;
1672
1673    return BW->bitmap.grid;
1674}
1675
1676void
1677BWSwitchGrid(Widget w)
1678{
1679    BitmapWidget BW = (BitmapWidget) w;
1680    BW->bitmap.grid ^= TRUE;
1681    BWDrawGrid(w,
1682	       0, 0,
1683	       BW->bitmap.image->width - 1, BW->bitmap.image->height - 1);
1684}
1685
1686void
1687BWGrid(Widget w, Boolean _switch)
1688{
1689    BitmapWidget BW = (BitmapWidget) w;
1690
1691    if (BW->bitmap.grid != _switch)
1692	BWSwitchGrid(w);
1693}
1694
1695Boolean
1696BWQueryDashed(Widget w)
1697{
1698    BitmapWidget BW = (BitmapWidget) w;
1699
1700    return (BW->bitmap.dashed);
1701}
1702
1703void
1704BWSwitchDashed(Widget w)
1705{
1706    BitmapWidget BW = (BitmapWidget) w;
1707    XRectangle rectangle;
1708
1709    BWRedrawGrid(w, 0, 0, BW->bitmap.width - 1, BW->bitmap.height - 1);
1710
1711    rectangle.x = 0;
1712    rectangle.y = 0;
1713    rectangle.width = BW->core.width;
1714    rectangle.height = BW->core.height;
1715
1716    XSetClipRectangles(XtDisplay(BW),
1717		       BW->bitmap.frame_gc,
1718		       0, 0,
1719		       &rectangle, 1,
1720		       Unsorted);
1721
1722    XDrawRectangle(XtDisplay(BW), XtWindow(BW),
1723		   BW->bitmap.frame_gc,
1724		   InWindowX(BW, 0) - 1, InWindowY(BW, 0) - 1,
1725		   InWindowX(BW, BW->bitmap.width) - InWindowX(BW, 0) + 1,
1726		   InWindowY(BW, BW->bitmap.height) - InWindowY(BW, 0) + 1);
1727
1728    BW->bitmap.dashed ^= True;
1729    XSetFillStyle(XtDisplay(BW), BW->bitmap.frame_gc,
1730		  (BW->bitmap.dashed ? FillStippled : FillSolid));
1731
1732    XDrawRectangle(XtDisplay(BW), XtWindow(BW),
1733		   BW->bitmap.frame_gc,
1734		   InWindowX(BW, 0) - 1, InWindowY(BW, 0) - 1,
1735		   InWindowX(BW, BW->bitmap.width) - InWindowX(BW, 0) + 1,
1736		   InWindowY(BW, BW->bitmap.height) - InWindowY(BW, 0) + 1);
1737
1738    BWUnclip(w);
1739
1740    BWRedrawGrid(w, 0, 0, BW->bitmap.width - 1, BW->bitmap.height - 1);
1741}
1742
1743void
1744BWDashed(Widget w, Boolean _switch)
1745{
1746    BitmapWidget BW = (BitmapWidget) w;
1747
1748    if (BW->bitmap.dashed != _switch)
1749	BWSwitchDashed(w);
1750}
1751
1752static Boolean
1753SetValues(Widget old, Widget request, Widget new,
1754	  ArgList args, Cardinal *num_args) /* ARGSUSED */
1755{
1756  BitmapWidget oldbw = (BitmapWidget) old;
1757  BitmapWidget newbw = (BitmapWidget) new;
1758  Boolean resize = False;
1759  Boolean redisplay = False;
1760
1761#define NE(field) (oldbw->field != newbw->field)
1762
1763  if (NE(bitmap.grid))
1764    BWSwitchGrid(old);
1765
1766  if (NE(bitmap.dashed))
1767    BWSwitchDashed(old);
1768
1769  if (NE(bitmap.axes))
1770    BWSwitchAxes(old);
1771
1772  if (NE(bitmap.stippled))
1773    BWSwitchStippled(old);
1774
1775  if (NE(bitmap.proportional))
1776    resize = True;
1777
1778  if (NE(bitmap.filename) || NE(bitmap.basename)  || NE(bitmap.size))
1779    BWChangeNotify(old);
1780
1781  if (NE(bitmap.filename)) {
1782    if (newbw->bitmap.filename) {
1783      XtFree(oldbw->bitmap.filename);
1784      newbw->bitmap.filename = XtNewString(newbw->bitmap.filename);
1785    }
1786    else
1787      newbw->bitmap.filename = oldbw->bitmap.filename;
1788  }
1789
1790  if (NE(bitmap.basename)) {
1791    if (newbw->bitmap.basename) {
1792      XtFree(oldbw->bitmap.basename);
1793      newbw->bitmap.basename = XtNewString(newbw->bitmap.basename);
1794    }
1795    else
1796      newbw->bitmap.basename = oldbw->bitmap.basename;
1797  }
1798
1799  if (NE(bitmap.size)) {
1800    Dimension width, height;
1801
1802    if (BWParseSize(newbw->bitmap.size, &width, &height)) {
1803      ResizeGrid(newbw, width, height);
1804      resize = True;
1805    }
1806  }
1807
1808  if (NE(bitmap.margin) ||
1809      NE(bitmap.grid_tolerance) ||
1810      NE(bitmap.squareW) ||
1811      NE(bitmap.squareH) ||
1812      NE(core.height) ||
1813      NE(core.width))
1814    resize = True;
1815
1816  if (NE(bitmap.hot.x) || NE(bitmap.hot.y))
1817    BWSetHotSpot(old, newbw->bitmap.hot.x, newbw->bitmap.hot.y);
1818
1819  if (NE(bitmap.foreground_pixel) || NE(core.background_pixel)) {
1820    XSetForeground(XtDisplay(new),
1821		   newbw->bitmap.drawing_gc,
1822		   newbw->bitmap.foreground_pixel
1823		   ^
1824		   newbw->core.background_pixel);
1825    redisplay = True;
1826  }
1827
1828  if (NE(bitmap.frame_pixel) || NE(core.background_pixel)) {
1829    XSetForeground(XtDisplay(new),
1830		   newbw->bitmap.frame_gc,
1831		   newbw->bitmap.frame_pixel
1832		   ^
1833		   newbw->core.background_pixel);
1834    redisplay = True;
1835  }
1836
1837  if (NE(bitmap.dashes)) {
1838    XSetStipple(XtDisplay(new),
1839		newbw->bitmap.frame_gc,
1840		newbw->bitmap.dashes);
1841    redisplay = True;
1842  }
1843
1844  if (NE(bitmap.highlight_pixel) || NE(core.background_pixel)) {
1845    RedrawStippled(newbw);
1846    XSetForeground(XtDisplay(new),
1847		   newbw->bitmap.highlighting_gc,
1848		   newbw->bitmap.highlight_pixel
1849		   ^
1850		   newbw->core.background_pixel);
1851    RedrawStippled(newbw);
1852  }
1853
1854  if (NE(bitmap.stipple)) {
1855    RedrawStippled(newbw);
1856    XSetStipple(XtDisplay(new),
1857		newbw->bitmap.highlighting_gc,
1858		newbw->bitmap.stipple);
1859    RedrawStippled(newbw);
1860  }
1861
1862  if (resize) Resize((Widget)newbw);
1863
1864    return (redisplay || resize);
1865
1866#undef NE
1867}
1868
1869Boolean
1870BWQueryProportional(Widget w)
1871{
1872    BitmapWidget BW = (BitmapWidget) w;
1873
1874    return (BW->bitmap.proportional);
1875}
1876
1877void
1878BWSwitchProportional(Widget w)
1879{
1880    BitmapWidget BW = (BitmapWidget) w;
1881
1882    BW->bitmap.proportional ^= True;
1883
1884    Resize((Widget)BW);
1885    if (BW->core.visible)
1886	XClearArea(XtDisplay(BW), XtWindow(BW),
1887		   0, 0,
1888		   BW->core.width, BW->core.height,
1889		   True);
1890}
1891
1892#if 0
1893void
1894BWProportional(Widget w, Boolean _switch)
1895{
1896    BitmapWidget BW = (BitmapWidget) w;
1897
1898    if (BW->bitmap.proportional != _switch)
1899	BWSwitchProportional(w);
1900}
1901#endif
1902
1903void
1904BWTPaste(Widget w, XEvent *event, String *params, Cardinal *num_params)
1905{
1906    BitmapWidget BW = (BitmapWidget) w;
1907
1908    BWRequestSelection(w, event->xbutton.time, TRUE);
1909
1910    if (!BWQueryStored(w))
1911	return;
1912
1913    BWEngageRequest(w, RestoreRequest, False,
1914		    (char *)&(event->xbutton.state), sizeof(int));
1915
1916    OnePointHandler(w,
1917	       (BWStatus*) BW->bitmap.request_stack[BW->bitmap.current].status,
1918	       event, NULL);
1919}
1920
1921void
1922BWTMark(Widget w, XEvent *event, String *params, Cardinal *num_params)
1923{
1924    BitmapWidget BW = (BitmapWidget) w;
1925
1926    BWEngageRequest(w, MarkRequest, False,
1927		    (char *)&(event->xbutton.state), sizeof(int));
1928    TwoPointsHandler(w,
1929            (BWStatus*) BW->bitmap.request_stack[BW->bitmap.current].status,
1930	     event, NULL);
1931
1932}
1933
1934void
1935BWTMarkAll(Widget w, XEvent *event, String *params, Cardinal *num_params)
1936{
1937    BWMarkAll(w);
1938
1939    BWGrabSelection(w, event->xkey.time);
1940}
1941
1942void
1943BWTUnmark(Widget w, XEvent *event, String *params, Cardinal *num_params)
1944{
1945    BWUnmark(w);
1946}
1947
1948/*****************************************************************************/
1949