1 /* +-------------------------------------------------------------------+ */
2 /* | Copyright 1992, 1993, David Koblas (koblas@netcom.com)	       | */
3 /* | Copyright 1995, 1996 Torsten Martinsen (bullestock@dk-online.dk)  | */
4 /* |								       | */
5 /* | Permission to use, copy, modify, and to distribute this software  | */
6 /* | and its documentation for any purpose is hereby granted without   | */
7 /* | fee, provided that the above copyright notice appear in all       | */
8 /* | copies and that both that copyright notice and this permission    | */
9 /* | notice appear in supporting documentation.	 There is no	       | */
10 /* | representations about the suitability of this software for	       | */
11 /* | any purpose.  this software is provided "as is" without express   | */
12 /* | or implied warranty.					       | */
13 /* |								       | */
14 /* +-------------------------------------------------------------------+ */
15 
16 /* $Id: fatBitsEdit.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
17 
18 #include <stdio.h>
19 
20 #include <X11/Intrinsic.h>
21 #include <X11/IntrinsicP.h>	/* for XtResizeWidget() */
22 #include <X11/StringDefs.h>
23 #include <X11/Shell.h>
24 #include <X11/cursorfont.h>
25 
26 #include "xaw_incdir/Command.h"
27 #include "xaw_incdir/Toggle.h"
28 #include "xaw_incdir/Viewport.h"
29 #include "xaw_incdir/Box.h"
30 #include "xaw_incdir/Form.h"
31 #include "xaw_incdir/Scrollbar.h"
32 
33 #ifndef NOSTDHDRS
34 #include <stdlib.h>
35 #include <unistd.h>
36 #endif
37 #include "Paint.h"
38 #include "PaintP.h"
39 #include "xpaint.h"
40 #include "palette.h"
41 #include "menu.h"
42 #include "messages.h"
43 #include "misc.h"
44 #include "region.h"
45 #include "protocol.h"
46 #include "graphic.h"
47 #include "ops.h"
48 
49 #define PADDING	4
50 #define BW	1
51 
52 typedef struct LocalInfo_s {
53     int curX, curY;
54     int offX, offY, baseX, baseY;
55     Widget cursor;		/* The Box widget */
56     Widget subpaint;		/* the paint widget inside the box */
57     Widget view;		/* the widget in the popup window */
58     Widget paint;		/* The source of this zoom */
59     Widget shell;
60 
61     int zoom;			/* paint widget (parent) zoom value */
62     Position spX, spY;		/* subpaint x, y (relative to Box widget) */
63     Dimension spBW;		/* subpaint borderwidth */
64     struct LocalInfo_s *next;
65 } LocalInfo;
66 
67 static LocalInfo *head = NULL;
68 
69 typedef struct {
70     int size;			/* Max # of line segments */
71     int nsegs;			/* Current # of line segments */
72     XSegment *segs;		/* The segments */
73 } FatLineCursor;
74 
75 /*
76  * Segments are allocated in chunks this size. The smallest brush
77  * in the current brush palette uses 9 segments.
78  */
79 #define FATCHUNK 16
80 
81 /*
82  * Info passed to FatCursorDraw().
83  */
84 typedef struct fcinfo {
85     Widget w;
86     Boolean drawn;
87     int lastX, lastY;
88     GC gcx;
89     int zoom;
90     struct fcinfo *next;
91 } FatCursorInfo;
92 
93 typedef struct {
94     FatCursorInfo *info;
95     Pixmap pixmap;		/* Pixmap used for building master */
96     FatLineCursor cursor;
97 } FatMasterInfo;
98 
99 static FatMasterInfo master =
100 {NULL};
101 
102 /*
103  * Add a line segment to fc.
104  * Reallocs if necessary.
105  */
106 static void
AddSegment(FatLineCursor * fc,int x1,int y1,int x2,int y2)107 AddSegment(FatLineCursor * fc, int x1, int y1, int x2, int y2)
108 {
109     XSegment *p;
110 
111     if (fc->nsegs >= fc->size) {
112 	fc->size += FATCHUNK;
113 	fc->segs = realloc(fc->segs, fc->size * sizeof(XSegment));
114     }
115     p = &fc->segs[fc->nsegs++];
116     p->x1 = x1;
117     p->y1 = y1;
118     p->x2 = x2;
119     p->y2 = y2;
120 }
121 
122 /*
123  * Given a pixmap, create a master vector cursor.
124  */
125 static void
CreateFatCursor(Widget paint,Pixmap cursor,FatLineCursor * fc)126 CreateFatCursor(Widget paint, Pixmap cursor, FatLineCursor * fc)
127 {
128     XImage *src;
129     int w, h, x, y, xoff, yoff;
130     Display *dpy = XtDisplay(paint);
131 
132     fc->nsegs = 0;
133     fc->size = FATCHUNK;
134     fc->segs = xmalloc(FATCHUNK * sizeof(XSegment));
135 
136     GetPixmapWHD(dpy, cursor, &w, &h, NULL);
137     src = XGetImage(dpy, cursor, 0, 0, w, h, AllPlanes, ZPixmap);
138 
139     xoff = w / 2;
140     yoff = h / 2;
141     for (y = 0; y < h; ++y)
142 	for (x = 0; x < w; ++x)
143 	    /*
144 	     * If we find a black pixel, check the four neighbours and
145 	     * place a line segment between the pixel and each white neighbour.
146 	     */
147 	    if (XGetPixel(src, x, y)) {
148 		/* If at the edge, always add a line */
149 		if ((x == 0) || (XGetPixel(src, x - 1, y) == 0))
150 		    AddSegment(fc, x - xoff, y - yoff, x - xoff, y - yoff + 1);
151 		if ((y == 0) || (XGetPixel(src, x, y - 1) == 0))
152 		    AddSegment(fc, x - xoff, y - yoff, x - xoff + 1, y - yoff);
153 		if ((x == w - 1) || (XGetPixel(src, x + 1, y) == 0))
154 		    AddSegment(fc, x - xoff + 1, y - yoff, x - xoff + 1, y - yoff + 1);
155 		if ((y == h - 1) || (XGetPixel(src, x, y + 1) == 0))
156 		    AddSegment(fc, x - xoff, y - yoff + 1, x - xoff + 1, y - yoff + 1);
157 	    }
158 }
159 
160 
161 static void
FatCursorDraw(Widget w,FatCursorInfo * l,XMotionEvent * event)162 FatCursorDraw(Widget w, FatCursorInfo * l, XMotionEvent * event)
163 {
164     int x, y, i, n, z;
165     XSegment *s;
166     Display *dpy = XtDisplay(w);
167     Window win = XtWindow(w);
168     static int drawing = 0;
169 
170     /*
171      * If someone is still drawing on the canvas, return
172      */
173     if (drawing) {
174 	if (event->type == ButtonRelease) {
175 	    drawing = 0;
176 	    l->drawn = 0;	/* Don't draw at old position */
177 	} else
178 	    return;
179     }
180     n = master.cursor.nsegs;
181     z = l->zoom;
182 
183     /*
184      * Erase the old cursor (if any)
185      */
186     if (l->drawn) {
187 	x = l->lastX;
188 	y = l->lastY;
189 	s = master.cursor.segs;
190         if (z>0)
191 	    for (i = 0; i < n; ++i, ++s)
192 	        XDrawLine(dpy, win, l->gcx,
193 		          x + z * s->x1, y + z * s->y1,
194 		          x + z * s->x2, y + z * s->y2);
195         else
196 	    for (i = 0; i < n; ++i, ++s)
197 	        XDrawLine(dpy, win, l->gcx,
198 			  x + s->x1/(-z), y + s->y1/(-z),
199 			  x + s->x2/(-z), y + s->y2/(-z));
200     }
201     /*
202      * If a button is pressed, leave the cursor undrawn
203      */
204     if (event->type == ButtonPress) {
205     	drawing = 1;
206 	return;
207     }
208     /*
209      * If we're leaving the window, only draw the cursor once
210      * when we enter again
211      */
212     if (event->type == LeaveNotify) {
213 	l->drawn = 0;
214 	return;
215     }
216     l->drawn = 1;
217     x = event->x;
218     y = event->y;
219 
220     if (z>0) {
221         /* Snap to zoom grid */
222         x = x / z * z;
223         y = y / z * z;
224 	/* This can be confusing, so just forget it ... */
225         /*
226         if (event->x - x > z / 2)
227 	    x += z;
228         if (event->y - y > z / 2)
229 	    y += z;
230 	*/
231         s = master.cursor.segs;
232         for (i = 0; i < n; ++i, ++s)
233 	    XDrawLine(dpy, win, l->gcx,
234 		      x + z * s->x1, y + z * s->y1,
235 		      x + z * s->x2, y + z * s->y2);
236     } else {
237         /* Snap to zoom grid */
238         x = x * z * z;
239         y = y * z * z;
240         s = master.cursor.segs;
241         for (i = 0; i < n; ++i, ++s)
242 	    XDrawLine(dpy, win, l->gcx,
243 		      x + s->x1/(-z), y + s->y1/(-z),
244 		      x + s->x2/(-z), y + s->y2/(-z));
245     }
246 
247     l->lastX = x;
248     l->lastY = y;
249 }
250 
251 /*
252  * Register a fat cursor for the specified widget.
253  * If a fat cursor is already active, just update the zoom.
254  */
255 void
FatCursorAddZoom(int zoom,Widget winwid)256 FatCursorAddZoom(int zoom, Widget winwid)
257 {
258     FatCursorInfo *new, *p;
259 
260     for (p = master.info; p != NULL; p = p->next)
261 	if (p->w == winwid) {
262 	    p->zoom = zoom;
263 	    XtInsertEventHandler(winwid,
264 				 PointerMotionMask | ButtonPressMask |
265 				 ButtonReleaseMask | LeaveWindowMask,
266 				 False, (XtEventHandler) FatCursorDraw,
267 				 (XtPointer) p, XtListHead);
268 	    return;
269 	}
270     new = XtNew(FatCursorInfo);
271     new->next = master.info;
272     master.info = new;
273     new->zoom = zoom;
274     new->w = winwid;
275     new->gcx = GetGCX(winwid);
276     new->drawn = False;
277 
278     /*
279      * FatCursorDraw must be called before the drawing routine so that
280      * it can remove the cursor in time.
281      */
282     XtInsertEventHandler(winwid,
283 		  PointerMotionMask | LeaveWindowMask |
284 		  ButtonPressMask | ButtonReleaseMask,
285 		  False, (XtEventHandler) FatCursorDraw, (XtPointer) new,
286 			 XtListHead);
287 }
288 
289 /*
290  * Unregister a fat cursor for the specified widget's window.
291  */
292 void
FatCursorRemoveZoom(Widget winwid)293 FatCursorRemoveZoom(Widget winwid)
294 {
295     FatCursorInfo *p, *pp;
296 
297     for (p = pp = master.info; p != NULL; pp = p, p = p->next)
298 	if (p->w == winwid)
299 	    break;
300 
301     if (p == NULL)		/* not found */
302 	return;
303 
304     XtRemoveEventHandler(winwid, PointerMotionMask | ButtonPressMask |
305 			 ButtonReleaseMask | LeaveWindowMask,
306 			 False, (XtEventHandler) FatCursorDraw, (XtPointer) p);
307     if (pp == p)
308 	master.info = p->next;
309     else
310 	pp->next = p->next;
311     XtFree((XtPointer) p);
312 }
313 
314 void
FatCursorDestroyCallback(Widget w,XtPointer arg,XtPointer junk)315 FatCursorDestroyCallback(Widget w, XtPointer arg, XtPointer junk)
316 {
317     FatCursorRemoveZoom((Widget) arg);
318 }
319 
320 /*
321  * Rebuild the master vector cursor corresponding to the specified pixmap.
322  */
323 void
FatCursorSet(Widget w,Pixmap cursor)324 FatCursorSet(Widget w, Pixmap cursor)
325 {
326     if (master.pixmap != cursor) {	/* Avoid setting cursor more than once */
327 	master.pixmap = cursor;
328 	if (master.cursor.segs != NULL)
329 	    free(master.cursor.segs);
330 	CreateFatCursor(w, cursor, &master.cursor);
331     }
332 }
333 
334 /*
335  * If the specified widget has a FatCursor, remove its event handler.
336  */
337 void
FatCursorOff(Widget w)338 FatCursorOff(Widget w)
339 {
340     FatCursorInfo *p;
341 
342     for (p = master.info; p != NULL; p = p->next)
343 	if (p->w == w) {
344 	    XtRemoveEventHandler(p->w,
345 				 PointerMotionMask | ButtonPressMask |
346 				 ButtonReleaseMask | LeaveWindowMask,
347 				 False, (XtEventHandler) FatCursorDraw,
348 				 (XtPointer) p);
349 	    XtRemoveCallback(p->w, XtNdestroyCallback,
350 			     FatCursorDestroyCallback, p->w);
351 	    return;
352 	}
353 }
354 
355 /*
356 **  When the popup window is gone, free the storage
357  */
358 static void
destroyCallback(Widget w,XtPointer larg,void * junk2)359 destroyCallback(Widget w, XtPointer larg, void *junk2)
360 {
361     LocalInfo *l = (LocalInfo *) larg;
362     LocalInfo *c = head, **p = &head;
363 
364     while (c != NULL && c != l) {
365 	p = &c->next;
366 	c = c->next;
367     }
368 
369     if (c != NULL)
370 	*p = l->next;
371 
372     XtFree((XtPointer) l);
373 }
374 
375 /*
376 **  Done or WM-Close pressed, destroy widgets
377  */
378 static void
doneCallback(Widget w,XtPointer larg,void * junk2)379 doneCallback(Widget w, XtPointer larg, void *junk2)
380 {
381     LocalInfo *l = (LocalInfo *) larg;
382 
383     FatCursorRemoveZoom(l->view);
384 
385     /*
386     **	Destroy both the cursor and the popup window
387      */
388     XtDestroyWidget(l->cursor);
389     XtDestroyWidget(GetShell(w));
390 }
391 
392 /*
393 **  Move the cursor and the view window to the position
394 **   specified by x,y
395 **  w, h are dimensions of the zoomed area
396  */
397 static void
moveCursor(LocalInfo * l,int x,int y,int w,int h)398 moveCursor(LocalInfo * l, int x, int y, int w, int h)
399 {
400     int dw, dh;
401     int rx, ry;
402     int z;
403 
404     if (w == -1 || h == -1) {
405 	Dimension wt, ht;
406 
407 	XtVaGetValues(l->view, XtNzoom, &z,
408 		      XtNwidth, &wt,
409 		      XtNheight, &ht,
410 		      NULL);
411 	w = (wt + z - 1) / z;
412 	h = (ht + z - 1) / z;
413     }
414     XtVaGetValues(l->paint, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
415 
416     z = l->zoom;
417     if (z>0) {
418         h *= z;
419         w *= z;
420         dh *= z;
421         dw *= z;
422     } else {
423         h /= -z;
424         w /= -z;
425         dh /= -z;
426         dw /= -z;
427     }
428     if (x < 0)
429 	x = 0;
430     if (y < 0)
431 	y = 0;
432     if (x + w > dw)
433 	x = dw - w;
434     if (y + h > dh)
435 	y = dh - h;
436     if (x < 0 || y < 0)
437 	return;
438 
439     if (z>0) {
440         rx = x / z;		/* correct for parent's zoom factor */
441         ry = y / z;
442         x = rx * z;		/* snap view origin to zoom grid */
443         y = ry * z;
444     } else {
445         rx = x * (-z);		/* correct for parent's zoom factor */
446         ry = y * (-z);
447         x = rx / (-z);		/* snap view origin to zoom grid */
448         y = ry / (-z);
449     }
450 
451     XtVaSetValues(l->view, XtNzoomX, rx, XtNzoomY, ry, NULL);
452     XtVaSetValues(l->cursor, XtNx, x - l->spX - l->spBW - 1,
453 		  XtNy, y - l->spY - l->spBW - 1, NULL);
454     XtVaSetValues(l->subpaint, XtNzoomX, rx, XtNzoomY, ry, NULL);
455 }
456 
457 /*
458 **  Given the popup dimensions, set the "cursor" view to the position
459 **  chosen with the pointer
460  */
461 /*
462 static void
463 pointerCallback(Widget w, XtPointer larg, void *junk2)
464 {
465     LocalInfo *l = (LocalInfo *) larg;
466     Display *dpy;
467     int x, y, z, wt, ht;
468     Dimension width, height;
469     Cursor cursor;
470     XEvent event;
471     Window window;
472     Boolean done = 0;
473 
474     XtVaGetValues(l->view, XtNzoom, &z,
475                   XtNwidth, &width, XtNheight, &height, NULL);
476     wt = (width + z - 1) / z;
477     ht = (height + z - 1) / z;
478     dpy = XtDisplay(l->paint);
479     window = XtWindow(l->paint);
480     cursor = XCreateFontCursor(dpy, XC_crosshair);
481 
482     if (XGrabPointer(dpy, window, True,
483 		     ButtonPressMask,
484 		     GrabModeAsync, GrabModeAsync, None, cursor,
485 		     CurrentTime) != 0) {
486         fprintf(stderr, msgText[CANNOT_GRAB_POINTER]);
487 	return;
488     }
489     XFlush(dpy);
490     while (!done) {
491         XNextEvent(dpy, &event);
492 	if (event.type == ButtonRelease) {
493             if (event.xexpose.window != window) done = -1;
494 	    else
495             if (event.xbutton.button == 3) done = -1;
496 	    else {
497 	        x = event.xbutton.x - wt/2;
498 	        y = event.xbutton.y - ht/2;
499 		if (x<0) x = 0;
500 		if (y<0) y = 0;
501 		done = 1;
502 	    }
503 	}
504     }
505 
506     XUngrabPointer(dpy, CurrentTime);
507     if (done == 1)
508         moveCursor(l, x, y, -1, -1);
509 }
510 */
511 
512 /*
513 **  Given the popup dimensions, set the "cursor" view to the correct size
514  */
515 static void
resizeCursor(LocalInfo * l)516 resizeCursor(LocalInfo * l)
517 {
518     Dimension width, height;
519     int zoom;
520     int pw, ph;
521     int dw, dh;
522     int zx, zy;
523 
524     XtVaGetValues(l->view, XtNwidth, &width,
525 		  XtNheight, &height,
526 		  XtNzoom, &zoom,
527 		  NULL);
528     XtVaGetValues(l->paint, XtNdrawWidth, &dw,
529 		  XtNdrawHeight, &dh,
530 		  XtNzoom, &l->zoom,
531 		  NULL);
532     XtVaGetValues(l->subpaint, XtNzoomX, &zx,
533 		  XtNzoomY, &zy,
534 		  NULL);
535 
536     pw = (width + zoom - 1) / zoom;
537     ph = (height + zoom - 1) / zoom;
538     if ((pw + zx > dw) || (ph + zy > dh)) {
539 	int nx, ny;
540 
541 	nx = (pw + zx > dw) ? dw - pw : zx;
542 	ny = (ph + zy > dh) ? dh - ph : zy;
543 
544 	/*
545 	**  If the new x or y value off the screen, set it back.
546 	**    and resize view
547 	 */
548 	if (nx < 0 || ny < 0) {
549 	    if (nx < 0) {
550 		pw += nx;
551 		nx = 0;
552 	    }
553 	    if (ny < 0) {
554 		ph += ny;
555 		ny = 0;
556 	    }
557 	    /* XXX -- this really should be SetValues(width,height)
558 	    **	      but that doesn't work..?/
559 	     */
560 	    XtResizeWidget(l->view, pw * zoom, ph * zoom, 1);
561 	}
562 	moveCursor(l, nx, ny, pw, ph);
563     }
564 
565     if (l->zoom>0) {
566         pw *= l->zoom;
567         ph *= l->zoom;
568     } else {
569         pw /= -l->zoom;
570         ph /= -l->zoom;
571     }
572 
573     XtVaSetValues(l->cursor, XtNwidth, pw + 2 * (PADDING + BW),
574 		  XtNheight, ph + 2 * (PADDING + BW),
575 		  NULL);
576     XtVaSetValues(l->subpaint, XtNwidth, pw, XtNheight, ph, NULL);
577 }
578 
579 /*
580 **  Button down in the cursor window
581  */
582 static void
press(Widget w,LocalInfo * l,XButtonEvent * event,Boolean * flg)583 press(Widget w, LocalInfo * l, XButtonEvent * event, Boolean * flg)
584 {
585     Position x, y;
586     static long prev_time = 0;
587 
588     XtVaGetValues(w, XtNx, &x, XtNy, &y, NULL);
589     if (abs(event->time-prev_time) < 300)
590         RaiseWindow(XtDisplay(l->shell), XtWindow(l->shell));
591     prev_time = event->time;
592 
593     l->offX = event->x;
594     l->offY = event->y;
595     l->baseX = event->x_root - x;
596     l->baseY = event->y_root - y;
597     XtVaGetValues(l->paint, XtNzoom, &l->zoom, NULL);
598 }
599 
600 static void
motion(Widget w,LocalInfo * l,XMotionEvent * event,Boolean * flg)601 motion(Widget w, LocalInfo * l, XMotionEvent * event, Boolean * flg)
602 {
603     int nx, ny;
604     int px, py;
605 
606     /*
607     **	Compress motion events.
608      */
609     while (XCheckTypedWindowEvent(XtDisplay(w), XtWindow(w),
610 				  MotionNotify, (XEvent *) event));
611 
612     nx = event->x_root - l->baseX;
613     ny = event->y_root - l->baseY;
614 
615     px = nx + l->spX + l->spBW + 1;
616     py = ny + l->spY + l->spBW + 1;
617 
618     moveCursor(l, px, py, -1, -1);
619 }
620 
621 /*
622 **  If the paint view size changes, update the parent paint window cursor size
623  */
624 static void
sizeChanged(Widget w,XtPointer l,XtPointer junk)625 sizeChanged(Widget w, XtPointer l, XtPointer junk)
626 {
627     resizeCursor((LocalInfo *) l);
628 }
629 
630 static void
refreshCallback(Widget w,XtPointer l,XtPointer junk)631 refreshCallback(Widget w, XtPointer l, XtPointer junk)
632 {
633      XRectangle rect;
634      LocalInfo *li =  (LocalInfo *) l;
635      PaintWidget pw = (PaintWidget) li->view;
636      /* brutal way : update entire window ... */
637      rect.x = 0;
638      rect.y = 0;
639      rect.width = pw->core.width;
640      rect.height = pw->core.height;
641      zoomUpdate(pw, True, &rect);
642      /* update region if any */
643      if (pw->paint.region.child)
644          PwRegionExpose(pw->paint.region.child, pw, NULL, NULL);
645 }
646 
647 static void
fatbitsExposed(Widget w,LocalInfo * l,XConfigureEvent * event,Boolean * flg)648 fatbitsExposed(Widget w, LocalInfo * l, XConfigureEvent * event, Boolean * flg)
649 {
650      XRectangle rect;
651      PaintWidget pw = (PaintWidget) w;
652      /* brutal way : update entire window ... */
653      rect.x = 0;
654      rect.y = 0;
655      rect.width = pw->core.width;
656      rect.height = pw->core.height;
657      zoomUpdate(pw, True, &rect);
658  }
659 
660 /*
661 **  The parent box widget changed size, resize to fit.
662  */
663 static void
fatbitsResized(Widget w,LocalInfo * l,XConfigureEvent * event,Boolean * flg)664 fatbitsResized(Widget w, LocalInfo * l, XConfigureEvent * event, Boolean * flg)
665 {
666     Dimension width, height, bw;
667     int zoom;
668 
669     XtVaGetValues(l->view, XtNzoom, &zoom, XtNborderWidth, &bw,
670 		  NULL);
671     XtVaGetValues(XtParent(XtParent(l->view)), XtNwidth, &width,
672 		  XtNheight, &height,
673 		  NULL);
674 
675     width -= (bw + 2) * 2;
676     height -= bw * 2;
677 
678     /* XXX -- this really should be SetValues(width,height)
679     **	      but that doesn't work..?/
680      */
681     XtMoveWidget(XtParent(l->view), 3, 30);
682     XtResizeWidget(XtParent(l->view),
683                             (width>0)?width:1,
684                             (height>0)?height:1, 0);
685     XtResizeWidget(l->view, (width>10)?width-10:1,
686                             (height>38)?height-38:1, 1);
687 
688     resizeCursor(l);
689 }
690 
691 /*
692 **  One of the zoom percentage buttons pressed
693  */
694 static void
buttonCallback(Widget w,XtPointer lArg,void * junk2)695 buttonCallback(Widget w, XtPointer lArg, void *junk2)
696 {
697     LocalInfo *l = (LocalInfo *) lArg;
698     char *lbl;
699     int nz;
700     int cx, cy, zx, zy, z, nw, nh;
701     int x, y;
702     Dimension width, height;
703     Boolean state;
704 
705     XtVaGetValues(w, XtNstate, &state, XtNlabel, &lbl, NULL);
706 
707     if (state == False)
708 	return;
709 
710     nz = 0;
711     nz = atoi(lbl);
712     if (nz == 0)
713 	return;
714 
715     XtVaGetValues(l->view, XtNzoomX, &zx,
716 		  XtNzoomY, &zy,
717 		  XtNzoom, &z,
718 		  XtNwidth, &width,
719 		  XtNheight, &height,
720 		  NULL);
721 
722     if ((CurrentOp->add == BrushAdd) || (CurrentOp->add == EraseAdd) ||
723 	(CurrentOp->add == SmearAdd))
724 	FatCursorAddZoom(nz, l->view);
725 
726     cx = zx + ((width + z - 1) / z) / 2;
727     cy = zy + ((height + z - 1) / z) / 2;
728     nw = (width + nz - 1) / nz;
729     nh = (height + nz - 1) / nz;
730 
731     XtVaSetValues(l->view, XtNzoom, nz, NULL);
732     /*	center on image center */
733     x = cx - nw / 2;
734     y = cy - nh / 2;
735 
736     moveCursor(l, x, y, width / nz, height / nz);
737 }
738 
739 /*
740  * Called when the zoom factor or size of the canvas is changed,
741  * so that the FatBits cursor window can be resized.
742  * If zoom is -1, don't change zoom factor, just redisplay
743  * cursor window.
744  */
745 void
FatbitsUpdate(Widget w,int zoom)746 FatbitsUpdate(Widget w, int zoom)
747 {
748     LocalInfo *l;
749     int x, y, vw, vh, dw, dh;
750 
751 
752     /* find fatbits info structure for this popup */
753     for (l = head; l != NULL; l = l->next)
754 	if (l->paint == w) {
755 	    if (zoom != 0)
756 		l->zoom = zoom;	/* new parent zoom factor */
757 	    else
758 		zoom = l->zoom;
759 
760 	    resizeCursor(l);	/* correct cursor window size */
761 
762 	    /* reposition cursor window in parent paint window */
763 	    XtVaGetValues(l->view, XtNzoomX, &x, XtNzoomY, &y, NULL);
764             if (zoom>0)
765 	        XtVaSetValues(l->cursor, XtNx, x * zoom - l->spX - l->spBW - 1,
766 			      XtNy, y * zoom - l->spY - l->spBW - 1, NULL);
767             else
768 	        XtVaSetValues(l->cursor,
769                               XtNx, x / (-zoom) - l->spX - l->spBW - 1,
770 			      XtNy, y / (-zoom) - l->spY - l->spBW - 1, NULL);
771 
772 	    XtVaGetValues(w, XtNdrawWidth, &dw, XtNdrawHeight, &dh, NULL);
773 	    /* The Viewport is grandparent of the paint widget */
774 	    vw = vh = 0;
775 	    XtVaGetValues(XtParent(XtParent(w)),
776 			  XtNwidth, &vw, XtNheight, &vh, NULL);
777             if (zoom>0) {
778 	        if ((dw * zoom > vw) || (dh * zoom > vh))
779 		/*
780 		 * The zoomed canvas is larger than the viewport.
781 		 * Reposition view on canvas so cursor window remains visible.
782 		 * This solution is not perfect: the cursor window is simply
783 		 * positioned at (0,0) in the viewport window.
784 		 * Still, it's better than having a non-visible cursor window.
785 		 */
786 		 XawViewportSetCoordinates(XtParent(XtParent(w)),
787                                               x * zoom, y * zoom);
788 
789 	         return;	/* there can only be one fatbits popup */
790 	    }
791 
792             if (zoom<0) {
793 	        if ((dw /(-zoom) > vw) || (dh / (-zoom) > vh))
794 		    XawViewportSetCoordinates(XtParent(XtParent(w)),
795                                               x / (-zoom), y / (-zoom));
796 
797 	        return;
798 	    }
799 	}
800 }
801 
802 void
keyPress(Widget w,void * l,XKeyEvent * event,void * info)803 keyPress(Widget w, void * l, XKeyEvent * event, void * info)
804 {
805     KeySym keysym;
806     int len;
807     char buf[4];
808 
809     len = XLookupString(event, buf, sizeof(buf) - 1, &keysym, NULL);
810 
811     switch (keysym) {
812         case XK_Escape:
813             PopdownMenusGlobal();
814             break;
815         default:
816 	    break;
817     }
818 }
819 
FatbitsEditDestroy(Widget paint)820 void FatbitsEditDestroy(Widget paint)
821 {
822     LocalInfo *l;
823     for (l = head; l != NULL && l->paint != paint; l = l->next);
824     if (l != NULL)
825         doneCallback(l->shell, l, NULL);
826 }
827 
828 /*
829 **  Construct the fatbits popup.
830  */
831 void
FatbitsEdit(Widget paint)832 FatbitsEdit(Widget paint)
833 {
834     Widget shell, form, fat;
835     Widget done, box, prev=None, *zoombut;
836     Widget refresh;
837     int i, zoom;
838     Colormap cmap;
839     Position x, y, lx, ly;
840     Dimension width = 48, height = 48;
841     WidgetList wlist;
842     LocalInfo *l;
843     char *ptr;
844     Arg args[4];
845     int nargs = 0;
846     static char *zoomList[] =
847     {
848 	"zoomButton1",
849 	"zoomButton2",
850 	"zoomButton3",
851 	"zoomButton4",
852 	"zoomButton5",
853     };
854     XtTranslations trans =
855     XtParseTranslationTable("<BtnDown>,<BtnUp>: set() notify()");
856 
857     /* If a popup for this paint widget exists, raise it */
858     for (l = head; l != NULL && l->paint != paint; l = l->next);
859 
860     if (l != NULL) {
861 	RaiseWindow(XtDisplay(l->shell), XtWindow(l->shell));
862 	return;
863     }
864     /* Construct a new fatbits popup */
865 
866     l = XtNew(LocalInfo);
867 
868     l->paint = paint;
869     l->next = head;
870     head = l;
871 
872     XtVaGetValues(paint, XtNcolormap, &cmap, XtNzoom, &l->zoom,
873 		  XtNdownX, &lx, XtNdownY, &ly, NULL);
874 
875     XtSetArg(args[nargs], XtNcolormap, cmap); nargs++;
876 
877     shell = XtVisCreatePopupShell("fatbits", topLevelShellWidgetClass,
878 				  GetToplevel(paint), args, nargs);
879 
880     l->shell = shell;
881 
882     PaletteAddUser(PaletteFind(shell, cmap), shell);
883     form = XtVaCreateManagedWidget("form", formWidgetClass, shell, NULL);
884 
885     done = XtVaCreateManagedWidget("done", commandWidgetClass, form,
886 				     /*
887 				     XtNfromVert, box,
888 				     XtNtop, XtChainBottom,
889 				     XtNbottom, XtChainBottom,
890 				     XtNleft, XtChainLeft,
891 				     XtNright, XtChainLeft,
892 				     */
893 				     NULL);
894 
895     XtAddCallback(done, XtNcallback, doneCallback, (XtPointer) l);
896 
897     zoombut = (Widget *) xmalloc(XtNumber(zoomList) * sizeof(Widget));
898     for (i = 0; i < XtNumber(zoomList); i++) {
899 	zoombut[i] = XtVaCreateManagedWidget(zoomList[i],
900 					 toggleWidgetClass, form,
901 					 XtNheight, 22,
902 					 XtNfromHoriz, (i)?prev:done,
903 					 XtNradioGroup, prev,
904 					 XtNtranslations, trans,
905 					 NULL);
906 	prev = zoombut[i];
907 	XtAddCallback(prev, XtNcallback, buttonCallback, (XtPointer) l);
908 
909 	if (i == XtNumber(zoomList) / 2) {
910 	    XtVaSetValues(prev, XtNstate, True, NULL);
911 	}
912     }
913 
914     refresh = XtVaCreateManagedWidget("refresh", commandWidgetClass, form,
915                                       XtNfromHoriz,
916                                           zoombut[XtNumber(zoomList)-1],
917 				      NULL);
918     XtAddCallback(refresh, XtNcallback, refreshCallback, (XtPointer) l);
919 
920     box = XtVaCreateManagedWidget("paintBox", boxWidgetClass, form,
921 			  XtNbackgroundPixmap, GetBackgroundPixmap(form),
922 			  XtNvertDistance, 30,
923 			  NULL);
924 
925     XtAddEventHandler(box, StructureNotifyMask, False,
926 		      (XtEventHandler) fatbitsResized, (XtPointer) l);
927 
928     fat = XtVaCreateManagedWidget("paint", paintWidgetClass, box,
929 				  XtNpaint, paint,
930 				  XtNbottom, XtChainBottom,
931 				  NULL);
932 
933     l->view = fat;
934     XtVaGetValues(fat, XtNzoom, &zoom, NULL);
935 
936     XtVaSetValues(fat, XtNwidth, width * zoom, XtNheight, height * zoom, NULL);
937 
938     XtAddCallback(shell, XtNdestroyCallback, destroyCallback, (XtPointer) l);
939     AddDestroyCallback(shell, (DestroyCallbackFunc) doneCallback, (XtPointer) l);
940 
941     XtAddCallback(fat, XtNdestroyCallback, FatCursorDestroyCallback, fat);
942     XtAddEventHandler(fat, ExposureMask, False,
943 		      (XtEventHandler) fatbitsExposed, (XtPointer) l);
944 
945     ccpAddStdPopup(fat, NULL);
946 
947     XtPopup(shell, XtGrabNone);
948     SetWMInputHint(XtDisplay(shell), XtWindow(shell));
949 
950     XtVaGetValues(shell, XtNtitle, &ptr, NULL);
951     StoreName(shell, ptr);
952 
953     lx -= width/2;
954     ly -= height/2;
955     if (lx < 0) lx = 0;
956     if (ly < 0) ly = 0;
957 
958     i = PADDING + 2;
959 
960     if (l->zoom>0) {
961         x = lx * l->zoom - i;
962         y = ly * l->zoom - i;
963     } else {
964         x = lx / (-l->zoom) - i;
965         y = ly / (-l->zoom) - i;
966     }
967 
968     box = XtVaCreateManagedWidget("paintBox", boxWidgetClass, paint,
969 				  XtNx, x,
970 				  XtNy, y,
971 				  XtNwidth, width + 2 * (PADDING + 1),
972 				  XtNheight, height + 2 * (PADDING + 1),
973 				  XtNborderWidth, 1,
974 			          XtNbackgroundPixmap,
975                                          GetBackgroundPixmap(paint),
976 				  NULL);
977 
978     l->cursor = box;
979 
980     XtAddEventHandler(box, ButtonPressMask, False,
981 		      (XtEventHandler) press, (XtPointer) l);
982     XtAddEventHandler(box, ButtonMotionMask, False,
983 		      (XtEventHandler) motion, (XtPointer) l);
984 
985     l->subpaint = XtVaCreateManagedWidget("fatPaint", paintWidgetClass, box,
986 					  XtNpaint, paint,
987 					  XtNzoom, PwZoomParent,
988 					  XtNx, PADDING,
989 					  XtNy, PADDING,
990 					  XtNborderWidth, 1,
991 					  XtNwidth, width,
992 					  XtNheight, height,
993 					  XtVaTypedArg, XtNcursor,
994 				          XtRString, "fleur", sizeof(Cursor),
995 					  NULL);
996 
997     XtVaGetValues(l->subpaint, XtNx, &l->spX,
998 		  XtNy, &l->spY,
999 		  XtNborderWidth, &l->spBW,
1000 		  NULL);
1001 
1002     XtVaSetValues(l->subpaint, XtNzoomX, lx, XtNzoomY, ly, NULL);
1003     XtVaSetValues(l->view, XtNzoomX, lx, XtNzoomY, ly, NULL);
1004     XtAddCallback(l->view, XtNsizeChanged, sizeChanged, (XtPointer) l);
1005 
1006     /*
1007     **	Set the current operator
1008      */
1009     StateAddParent(shell, paint);
1010 
1011     XtUnmanageChild(done);
1012     XMapWindow(XtDisplay(done), XtWindow(done));
1013     XtUnmanageChild(box);
1014     XMapWindow(XtDisplay(box), XtWindow(box));
1015     XtUnmanageChild(refresh);
1016     XMapWindow(XtDisplay(refresh), XtWindow(refresh));
1017     XFlush(XtDisplay(box));
1018 
1019     for (i=0; i<XtNumber(zoomList); i++) {
1020         XtUnmanageChild(zoombut[i]);
1021         XMapWindow(XtDisplay(zoombut[i]), XtWindow(zoombut[i]));
1022     }
1023     XFlush(XtDisplay(box));
1024     usleep(30000);
1025     XtSetMinSizeHints(l->shell, 214, 108);
1026     XtAddEventHandler(form, KeyPressMask | KeyReleaseMask,
1027 		      False, (XtEventHandler) keyPress, NULL);
1028 
1029     wlist = InitializedWidgetList();
1030     XtVaSetValues(fat, XtNmenuwidgets, wlist, NULL);
1031     CheckMarkItems(fat, wlist);
1032     return;
1033 }
1034