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