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