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: PaintUndo.c,v 1.17 2005/03/20 20:15:32 demailly Exp $ */
17 
18 #include <X11/IntrinsicP.h>
19 #include <stdlib.h>
20 #include "xpaint.h"
21 #include "misc.h"
22 #include "PaintP.h"
23 
24 /* #define DEBUG */
25 
26 #define	GET_PW(w)	((PaintWidget)(((PaintWidget)w)->paint.paint == None ? \
27 				       w : ((PaintWidget)w)->paint.paint))
28 
29 static AlphaPixmap NULLAP = { None, NULL};
30 
31 static void UndoPush(PaintWidget pw, AlphaPixmap p);
32 static void RedoClear(PaintWidget pw);
33 static void RedoPush(PaintWidget pw, AlphaPixmap p);
34 static AlphaPixmap UndoPop(PaintWidget pw);
35 static AlphaPixmap RedoPop(PaintWidget pw);
36 
37 
38 void
UndoInitialize(PaintWidget pw,int n)39 UndoInitialize(PaintWidget pw, int n)
40 {
41     int i;
42     pw->paint.nlevels = n;
43     pw->paint.undostack = n ? xmalloc(n*sizeof(AlphaPixmap)) : NULL;
44     pw->paint.redostack = n ? xmalloc(n*sizeof(AlphaPixmap)) : NULL;
45     pw->paint.undobot = pw->paint.undoitems = 0;
46     pw->paint.redobot = pw->paint.redoitems = 0;
47     for (i=0; i<n; i++) {
48         pw->paint.undostack[i].pixmap = None;
49         pw->paint.undostack[i].alpha = NULL;
50     }
51 }
52 
53 static void
RedoClear(PaintWidget pw)54 RedoClear(PaintWidget pw)
55 {
56     AlphaPixmap p;
57 
58     do {
59         p = RedoPop(pw);
60     } while (p.pixmap != None);
61 }
62 
63 static void
UndoPush(PaintWidget pw,AlphaPixmap p)64 UndoPush(PaintWidget pw, AlphaPixmap p)
65 {
66     UndoStack * s, * h;
67 
68     if (pw->paint.nlevels == 0)
69 	return;
70 
71 #ifdef DEBUG
72     printf("push : %d %d\n", p.pixmap, p.alpha);
73 #endif
74 
75     h = pw->paint.head;
76     s = h;
77     do {
78 	if (s->alphapix.pixmap == p.pixmap) {
79 	    s->pushed = True;
80 	    pw->paint.undo = s;
81 	    break;
82 	}
83 	s = s->next;
84     } while (s != h);
85     if (pw->paint.undoitems < pw->paint.nlevels)
86 	pw->paint.undostack[pw->paint.undoitems++] = p;
87     else {
88 	int bot = pw->paint.undobot;
89 	AlphaPixmap bp;
90 
91 	bp = pw->paint.undostack[bot];
92 	s = h;
93 	do {
94 	    if (s->alphapix.pixmap == bp.pixmap) {
95 		s->pushed = False;
96 		break;
97 	    }
98 	    s = s->next;
99 	} while (s != h);
100 	pw->paint.undostack[bot++] = p;
101 	pw->paint.undobot = bot % pw->paint.nlevels;
102     }
103 }
104 
105 static AlphaPixmap
UndoPop(PaintWidget pw)106 UndoPop(PaintWidget pw)
107 {
108     AlphaPixmap p;
109     UndoStack * s, * h;
110 
111     if (pw->paint.undoitems == 0)
112         return NULLAP;
113     --pw->paint.undoitems;
114     p = pw->paint.undostack[(pw->paint.undobot + pw->paint.undoitems)
115 			   % pw->paint.nlevels];
116     h = pw->paint.head;
117     s = h;
118     do {
119 	if (s->alphapix.pixmap == p.pixmap) {
120 	    s->pushed = False;
121 	    break;
122 	}
123 	s = s->next;
124     } while (s != h);
125     return p;
126 }
127 
128 static void
RedoPush(PaintWidget pw,AlphaPixmap p)129 RedoPush(PaintWidget pw, AlphaPixmap p)
130 {
131     UndoStack * s, * h;
132 
133     if (pw->paint.nlevels == 0)
134 	return;
135     h = pw->paint.head;
136     s = h;
137     do {
138 	if (s->alphapix.pixmap == p.pixmap) {
139 	    s->pushed = True;
140 	    break;
141 	}
142 	s = s->next;
143     } while (s != h);
144     if (pw->paint.redoitems < pw->paint.nlevels)
145 	pw->paint.redostack[pw->paint.redoitems++] = p;
146     else {
147 	int bot = pw->paint.redobot;
148 	AlphaPixmap bp;
149 
150 	bp = pw->paint.redostack[bot];
151 	s = h;
152 	do {
153 	    if (s->alphapix.pixmap == bp.pixmap) {
154 		s->pushed = False;
155 		break;
156 	    }
157 	    s = s->next;
158 	} while (s != h);
159 
160 	pw->paint.redostack[bot++] = p;
161 	pw->paint.redobot = bot % pw->paint.nlevels;
162     }
163 }
164 
165 static AlphaPixmap
RedoPop(PaintWidget pw)166 RedoPop(PaintWidget pw)
167 {
168     AlphaPixmap p;
169     UndoStack * s, * h;
170 
171     if (pw->paint.redoitems == 0)
172 	return NULLAP;
173     --pw->paint.redoitems;
174     p = pw->paint.redostack[(pw->paint.redobot + pw->paint.redoitems)
175 			   % pw->paint.nlevels];
176     h = pw->paint.head;
177     s = h;
178     do {
179 	if (s->alphapix.pixmap == p.pixmap) {
180 	    s->pushed = False;
181 	    break;
182 	}
183 	s = s->next;
184     } while (s != h);
185     return p;
186 }
187 
188 void
Undo(Widget w)189 Undo(Widget w)
190 {
191     PaintWidget pw = GET_PW(w);
192     AlphaPixmap popped;
193 
194     popped = UndoPop(pw);
195     if (popped.pixmap == None) {
196 #ifdef ERRORBEEP
197 	XBell(XtDisplay(w), 100);
198 #endif
199 	return;
200     }
201     RedoPush(pw, pw->paint.current);
202     pw->paint.current = popped;
203 #ifdef DEBUG
204     printf("undo : %d %d\n", popped.pixmap, popped.alpha);
205 #endif
206     PwUpdate(w, NULL, True);
207 }
208 
209 void
Redo(Widget w)210 Redo(Widget w)
211 {
212     PaintWidget pw = GET_PW(w);
213     AlphaPixmap popped;
214 
215     popped = RedoPop(pw);
216     if (popped.pixmap == None) {
217 #ifdef ERRORBEEP
218 	XBell(XtDisplay(w), 100);
219 #endif
220 	return;
221     }
222     UndoPush(pw, pw->paint.current);
223     pw->paint.current = popped;
224     PwUpdate(w, NULL, True);
225 }
226 
227 /*
228  * Called when starting a drawing operation.
229  */
230 AlphaPixmap
PwUndoStart(Widget wid,XRectangle * rect)231 PwUndoStart(Widget wid, XRectangle * rect)
232 {
233     PaintWidget pw = GET_PW(wid);
234     UndoStack * s;
235     AlphaPixmap cur;
236     int x, y, w, h, m;
237 
238     pw->paint.dirty = True;
239 
240     cur.pixmap = GET_PIXMAP(pw);
241     cur.alpha = pw->paint.current.alpha;
242 
243     UndoPush(pw, cur);
244     RedoClear(pw);
245 
246     if (rect) {
247 	x = rect->x;
248 	y = rect->y;
249 	w = rect->width;
250 	h = rect->height;
251     } else {
252 	x = y = 0;
253 	w = pw->paint.drawWidth;
254 	h = pw->paint.drawHeight;
255     }
256 
257     for (s = pw->paint.head; s->next != pw->paint.head; s = s->next) {
258 	if (s->pushed == False)
259 	    break;
260     }
261 
262     s->box.x = x;
263     s->box.y = y;
264     s->box.width = w;
265     s->box.height = h;
266 
267     XCopyArea(XtDisplay(pw), cur.pixmap, s->alphapix.pixmap,
268         pw->paint.gc, 0, 0, pw->paint.drawWidth, pw->paint.drawHeight, 0, 0);
269 
270     if (cur.alpha) {
271         m = pw->paint.drawWidth * pw->paint.drawHeight;
272         if (!s->alphapix.alpha)
273 	    s->alphapix.alpha = (unsigned char *)XtMalloc(m);
274         memcpy(s->alphapix.alpha, cur.alpha, m);
275     }
276 
277     pw->paint.current = s->alphapix;
278     return s->alphapix;
279 }
280 
281 
282 void
UndoStart(Widget w,OpInfo * info)283 UndoStart(Widget w, OpInfo * info)
284 {
285     AlphaPixmap start;
286     if (info->surface != opPixmap)
287 	return;
288 
289     start = PwUndoStart(w, NULL);
290     info->drawable = start.pixmap;
291 }
292 
293 
294 void
UndoStartPoint(Widget w,OpInfo * info,int x,int y)295 UndoStartPoint(Widget w, OpInfo * info, int x, int y)
296 {
297     PaintWidget pw = GET_PW(w);
298 
299     if (info->surface != opPixmap)
300 	return;
301 
302     UndoStart(w, info);
303 
304     if (pw->paint.undo == NULL)
305 	return;
306 
307     pw->paint.undo->box.x = x - pw->paint.lineWidth;
308     pw->paint.undo->box.y = y - pw->paint.lineWidth;
309     pw->paint.undo->box.width = 1 + pw->paint.lineWidth * 2;
310     pw->paint.undo->box.height = 1 + pw->paint.lineWidth * 2;
311 }
312 
313 void
UndoGrow(Widget w,int x,int y)314 UndoGrow(Widget w, int x, int y)
315 {
316     PaintWidget pw = GET_PW(w);
317     XRectangle *rect;
318     int dx, dy;
319 
320 #ifdef DEBUG
321     printf("point %d %d\n", x, y);
322 #endif
323 
324     if (pw->paint.undo == NULL)
325 	return;
326 
327     rect = &pw->paint.undo->box;
328 
329     rect->x += pw->paint.lineWidth;
330     rect->y += pw->paint.lineWidth;
331     rect->width -= pw->paint.lineWidth * 2 + 1;
332     rect->height -= pw->paint.lineWidth * 2 + 1;
333 
334     dx = x - rect->x;
335     dy = y - rect->y;
336 
337     if (dx > 0) {
338 	rect->width = MAX(rect->width, dx);
339     } else {
340 	rect->width = (int) rect->width - dx;
341 	rect->x = x;
342     }
343 
344     if (dy > 0) {
345 	rect->height = MAX(rect->height, dy);
346     } else {
347 	rect->height = (int) rect->height - dy;
348 	rect->y = y;
349     }
350 
351     rect->x -= pw->paint.lineWidth;
352     rect->y -= pw->paint.lineWidth;
353     rect->width += pw->paint.lineWidth * 2 + 1;
354     rect->height += pw->paint.lineWidth * 2 + 1;
355 
356 #ifdef DEBUG
357     printf("undogrow (%d) %d %d [%d %d]\n",
358            rect, rect->x, rect->y, rect->width, rect->height);
359 #endif
360 }
361 
362 void
PwUndoSetRectangle(Widget w,XRectangle * rect)363 PwUndoSetRectangle(Widget w, XRectangle * rect)
364 {
365     PaintWidget pw = GET_PW(w);
366 
367     if (pw->paint.undo == NULL)
368 	return;
369 
370     pw->paint.undo->box = *rect;
371 }
372 void
PwUndoAddRectangle(Widget w,XRectangle * rect)373 PwUndoAddRectangle(Widget w, XRectangle * rect)
374 {
375     PaintWidget pw = GET_PW(w);
376 
377     if (pw->paint.undo == NULL)
378 	return;
379 
380     pw->paint.undo->box = *RectUnion(rect, &pw->paint.undo->box);
381 }
382 
383 void
UndoSetRectangle(Widget w,XRectangle * rect)384 UndoSetRectangle(Widget w, XRectangle * rect)
385 {
386     PaintWidget pw = GET_PW(w);
387 
388     if (pw->paint.undo == NULL)
389 	return;
390 
391     PwUndoSetRectangle(w, rect);
392 
393     pw->paint.undo->box.x -= pw->paint.lineWidth;
394     pw->paint.undo->box.y -= pw->paint.lineWidth;
395     pw->paint.undo->box.width += pw->paint.lineWidth * 2 + 1;
396     pw->paint.undo->box.height += pw->paint.lineWidth * 2 + 1;
397 }
398 
399 void
UndoStartRectangle(Widget w,OpInfo * info,XRectangle * rect)400 UndoStartRectangle(Widget w, OpInfo * info, XRectangle * rect)
401 {
402     UndoStart(w, info);
403     UndoSetRectangle(w, rect);
404 }
405