1 /* $Id: drawing.c,v 1.8 2005/01/27 02:48:45 meffie Exp $
2 *
3 * GNU Paint
4 * Copyright 2000-2003, 2007 Li-Cheng (Andy) Tai
5 *
6 * Authors: Li-Cheng (Andy) Tai <atai@gnu.org>
7 * Michael A. Meffie III <meffiem@neo.rr.com>
8 *
9 * This program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License
11 * as published by the Free Software Foundation; either version 3
12 * of the License, or (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be
15 * useful, but WITHOUT ANY WARRANTY; without even the implied
16 * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
17 * PURPOSE. See the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 */
22
23 #ifdef HAVE_CONFIG_H
24 # include <config.h>
25 #endif
26
27 #ifdef HAVE_STRING_H
28 # include <string.h>
29 #endif
30
31 #include "drawing.h"
32 #include "image.h"
33 #include "image_processing.h"
34 #include "debug.h"
35
36 #include <stdlib.h>
37 #include <stdio.h>
38 #include <gdk/gdkx.h> /* for gdk_root_parent */
39
40
41 #include <libintl.h>
42 #define _(String) gettext (String)
43
44
45 /* default filename */
46 #define UNTITLED _("untitled.png")
47
48 static void drawing_create_pixmap(gpaint_drawing *drawing, int w, int h);
49 static void drawing_update_title(gpaint_drawing *drawing);
50
51
52 void drawing_set_filename(gpaint_drawing *drawing, const gchar *filename);
53
54 gpaint_drawing*
drawing_new_blank(GtkDrawingArea * drawing_area,GdkGC * gc,gint width,gint height)55 drawing_new_blank(GtkDrawingArea *drawing_area, GdkGC *gc, gint width, gint height)
56 {
57 gpaint_drawing *drawing = (gpaint_drawing*)g_new0(gpaint_drawing, 1);
58 GtkWidget *widget = GTK_WIDGET(drawing_area);
59 g_assert(drawing);
60 g_assert(drawing_area);
61 g_assert(gc);
62
63 drawing->top_level = gtk_widget_get_toplevel(widget);
64 drawing->window = widget->window; /* reference to the drawing area window */
65
66 drawing->gc = gc; /* reference to the gc */
67 gdk_gc_ref(gc);
68
69 if (width==0 || height==0)
70 {
71 gdk_window_get_size(widget->window, &width, &height);
72 }
73
74 drawing->filename = g_string_new(UNTITLED);
75 drawing->untitled = TRUE; /* true until until the file is saved */
76 drawing_update_title(drawing);
77
78 /* create pixmap, which is stored in the X server's memory */
79 drawing_create_pixmap(drawing, width, height);
80
81 /* initial drawing is filled with white to look like a blank page */
82 gdk_draw_rectangle(
83 drawing->backing_pixmap,
84 drawing->top_level->style->white_gc,
85 TRUE,
86 0, 0,
87 drawing->width,
88 drawing->height );
89
90 debug1("new drawing=%p", drawing);
91 return drawing;
92 }
93
94 gpaint_drawing*
drawing_new_from_file(GtkDrawingArea * drawing_area,GdkGC * gc,const gchar * filename)95 drawing_new_from_file(GtkDrawingArea *drawing_area, GdkGC *gc, const gchar *filename)
96 {
97 gpaint_drawing *drawing = (gpaint_drawing*)g_new0(gpaint_drawing, 1);
98 GtkWidget *widget = GTK_WIDGET(drawing_area);
99 gpaint_image *image;
100
101 g_assert(drawing);
102 g_assert(drawing_area);
103 g_assert(gc);
104 g_assert(filename);
105 g_assert(*filename);
106
107 drawing->top_level = gtk_widget_get_toplevel(widget);
108 drawing->window = widget->window; /* reference to the drawing area window */
109 drawing->gc = gc; /* reference to the gc */
110 gdk_gc_ref(gc);
111
112 /* read the image from the file */
113 image = image_new_readfile(filename);
114 if (!image)
115 {
116 debug1("cannot open filename %s", filename);
117 return 0;
118 }
119
120 drawing->filename = g_string_new(UNTITLED);
121 drawing->untitled = TRUE;
122 drawing_set_filename(drawing, filename);
123
124 /* create pixmap, which is stored in the X server's memory */
125 drawing_create_pixmap(drawing, image_width(image), image_height(image));
126
127 /* render the image on to the backing pixmap */
128 image_draw_on_pixmap(image, &drawing->backing_pixmap, drawing->gc);
129 image_free(image); /* free the memory buffer */
130
131 debug1("new drawing=%p", drawing);
132 return drawing;
133 }
134
135 gpaint_drawing*
drawing_new_from_desktop(GtkDrawingArea * drawing_area,GdkGC * gc)136 drawing_new_from_desktop(GtkDrawingArea *drawing_area, GdkGC *gc)
137 {
138 gpaint_drawing *drawing = (gpaint_drawing*)g_new0(gpaint_drawing, 1);
139 GtkWidget *widget = GTK_WIDGET(drawing_area);
140 gpaint_image *image;
141
142 g_assert(drawing);
143 g_assert(drawing_area);
144 g_assert(gc);
145
146 drawing->top_level = gtk_widget_get_toplevel(widget);
147 drawing->window = widget->window; /* reference to the drawing area window */
148 drawing->gc = gc; /* reference to the gc */
149 gdk_gc_ref(gc);
150
151 image = image_new_from_desktop();
152 if (!image)
153 {
154 debug("cannot create image from desktop");
155 return 0;
156 }
157
158 drawing->filename = g_string_new(UNTITLED);
159 drawing->untitled = TRUE; /* true until until the file is saved */
160 drawing->modified = TRUE; /* true because we grabbed the image and haven't saved yet */
161 drawing_update_title(drawing);
162
163 /* create pixmap, which is stored in the X server's memory */
164 drawing_create_pixmap(drawing, image_width(image), image_height(image));
165
166 /* render the image on to the backing pixmap */
167 image_draw_on_pixmap(image, &drawing->backing_pixmap, drawing->gc);
168 image_free(image); /* free the memory buffer */
169
170 debug1("new drawing=%p", drawing);
171 return drawing;
172 }
173
174 void
drawing_destroy(gpaint_drawing * drawing)175 drawing_destroy(gpaint_drawing *drawing)
176 {
177 g_assert(drawing);
178 debug_fn1("drawing=%p", drawing);
179 gdk_gc_unref(drawing->gc);
180 gdk_pixmap_unref(drawing->backing_pixmap);
181 memset(drawing, 0xBEBE, sizeof(drawing)); /* debugging aid */
182 g_free(drawing);
183 }
184
185 int
drawing_in_bounds(gpaint_drawing * drawing,int x,int y)186 drawing_in_bounds(gpaint_drawing *drawing, int x, int y)
187 {
188 return (0<=x && x<=drawing->width && 0<=y && y<=drawing->height);
189 }
190
191 void
drawing_copy_to_desktop(gpaint_drawing * drawing)192 drawing_copy_to_desktop(gpaint_drawing *drawing)
193 {
194 GdkWindow *root = gdk_window_lookup(gdk_x11_get_default_root_xwindow());
195 int width, height;
196
197 debug_fn();
198 gdk_window_get_size(root, &width, &height);
199 gdk_window_set_back_pixmap(root, drawing->backing_pixmap, FALSE);
200 gdk_window_clear(root);
201 }
202
203 int
drawing_save(gpaint_drawing * drawing)204 drawing_save(gpaint_drawing *drawing)
205 {
206 gchar *filename = drawing->filename->str;
207
208 /* Do not clobber existing file without consent. */
209 if (drawing->untitled)
210 {
211 if (g_file_test(filename, G_FILE_TEST_EXISTS))
212 {
213 GtkWidget *dialog;
214 gint result;
215 dialog = gtk_message_dialog_new(
216 GTK_WINDOW(drawing->top_level),
217 GTK_DIALOG_MODAL,
218 GTK_MESSAGE_QUESTION,
219 GTK_BUTTONS_YES_NO,
220 _("The file %s already exists.\n\nDo you want to overwrite it?"),
221 filename);
222 result = gtk_dialog_run(GTK_DIALOG(dialog));
223 gtk_widget_destroy(GTK_WIDGET(dialog));
224 if (result==GTK_RESPONSE_NO)
225 {
226 return FALSE;
227 }
228 }
229 }
230
231
232 return drawing_save_as(drawing, filename);
233 }
234
235 int
drawing_save_as(gpaint_drawing * drawing,const gchar * filename)236 drawing_save_as(gpaint_drawing *drawing, const gchar *filename)
237 {
238 gpaint_image *image = NULL;
239 GError *error = NULL;
240 int saved = FALSE;
241
242
243 g_assert(drawing);
244 image = drawing_create_image(drawing);
245 saved = image_write(image, filename, &error);
246 image_free(image);
247
248 if (saved)
249 {
250 drawing->modified = FALSE;
251 drawing_set_filename(drawing, filename);
252 }
253 else
254 {
255 GtkWidget *dialog = gtk_message_dialog_new(
256 GTK_WINDOW(drawing->top_level),
257 GTK_DIALOG_MODAL,
258 GTK_MESSAGE_WARNING,
259 GTK_BUTTONS_OK,
260 _("Failed to save file %s.\n\n%s"),
261 drawing->filename->str,
262 (error && error->message) ? error->message : "");
263 gtk_dialog_run(GTK_DIALOG(dialog));
264 gtk_widget_destroy(GTK_WIDGET(dialog));
265 g_free(error); /* allocated by gdk-pixbuf library */
266 }
267 return saved;
268 }
269
270 void
drawing_modified(gpaint_drawing * drawing)271 drawing_modified(gpaint_drawing *drawing)
272 {
273 if (!drawing->modified)
274 {
275 drawing->modified = TRUE;
276 drawing_update_title(drawing);
277 }
278 }
279
280 void
drawing_set_filename(gpaint_drawing * drawing,const gchar * filename)281 drawing_set_filename(gpaint_drawing *drawing, const gchar *filename)
282 {
283 g_assert(drawing);
284 g_assert(filename);
285 g_assert(drawing->filename);
286
287 if (!*filename)
288 {
289 g_warning("refusing to set empty filename.");
290 return;
291 }
292 debug1("filename=[%s]", filename);
293 g_string_assign(drawing->filename, filename);
294 drawing->untitled = FALSE;
295 drawing_update_title(drawing);
296 }
297
298 static void
drawing_update_title(gpaint_drawing * drawing)299 drawing_update_title(gpaint_drawing *drawing)
300 {
301 GString *title;
302 gchar *basename;
303
304 /* Display the filename in the title bar. */
305 title = g_string_new("");
306 if (drawing->untitled)
307 {
308 g_string_append(title, _("Untitled"));
309 }
310 else
311 {
312 basename = g_strrstr(drawing->filename->str, "/");
313 if (basename)
314 {
315 g_string_append(title, basename+1);
316 }
317 else
318 {
319 g_string_append(title, drawing->filename->str);
320 }
321 }
322 if (drawing->modified)
323 {
324 g_string_append(title, _(" (modified)"));
325 }
326 g_string_append(title, _(" - gpaint"));
327 gtk_window_set_title(GTK_WINDOW(drawing->top_level), title->str);
328 g_string_free(title, TRUE);
329 }
330
331 gpaint_image *
drawing_create_image(gpaint_drawing * drawing)332 drawing_create_image(gpaint_drawing *drawing)
333 {
334 GdkRectangle area = {0, 0, drawing->width, drawing->height};
335 gpaint_image *img = image_new_from_pixmap(drawing->backing_pixmap, area);
336 return img;
337 }
338
339 void
drawing_clear(gpaint_drawing * drawing)340 drawing_clear(gpaint_drawing *drawing)
341 {
342 GdkGCValues gcvalues;
343 gdk_gc_get_values(drawing->gc, &gcvalues);
344 gdk_gc_set_foreground(drawing->gc, &(gcvalues.background));
345 gdk_draw_rectangle(
346 drawing->backing_pixmap,
347 drawing->gc,
348 TRUE,
349 0, 0,
350 drawing->width,
351 drawing->height );
352 gdk_gc_set_foreground(drawing->gc, &(gcvalues.foreground));
353 }
354
355 void
drawing_clear_selection(gpaint_drawing * drawing,gpaint_point_array * points)356 drawing_clear_selection(gpaint_drawing *drawing, gpaint_point_array *points)
357 {
358 GdkGCValues gcvalues;
359 GdkRectangle clientrect;
360
361 if (point_array_size(points) > 2)
362 {
363 clientrect.x = clientrect.y = 0;
364 clientrect.width = drawing->width;
365 clientrect.height = drawing->height;
366
367 gdk_gc_get_values(drawing->gc, &gcvalues);
368 gdk_gc_set_clip_origin(drawing->gc, 0, 0);
369 gdk_gc_set_clip_rectangle(drawing->gc, &clientrect);
370
371 gdk_gc_set_foreground(drawing->gc, &(gcvalues.background));
372
373 gdk_draw_polygon(
374 drawing->backing_pixmap,
375 drawing->gc,
376 TRUE,
377 point_array_data(points),
378 point_array_size(points));
379 gdk_draw_polygon(
380 drawing->window,
381 drawing->gc,
382 TRUE,
383 point_array_data(points),
384 point_array_size(points));
385
386 gdk_gc_set_foreground(drawing->gc, &(gcvalues.foreground));
387 }
388 }
389
390
391 static void
drawing_create_pixmap(gpaint_drawing * drawing,int w,int h)392 drawing_create_pixmap(gpaint_drawing *drawing, int w, int h)
393 {
394 GdkRectangle rect;
395
396 /* create pixmap, which is stored in the X server's memory */
397 drawing->width = w;
398 drawing->height = h;
399 drawing->backing_pixmap = gdk_pixmap_new(
400 drawing->top_level->window,
401 drawing->width,
402 drawing->height, -1);
403 g_assert(drawing->backing_pixmap);
404 rect.x = 0;
405 rect.y = 0;
406 rect.width = drawing->width;
407 rect.height = drawing->height;
408 gdk_gc_set_clip_origin(drawing->gc, 0, 0);
409 gdk_gc_set_clip_rectangle(drawing->gc, &rect);
410 }
411
412 gboolean
drawing_prompt_to_save(gpaint_drawing * drawing)413 drawing_prompt_to_save(gpaint_drawing *drawing)
414 {
415 gboolean cancel = FALSE;
416 debug_fn();
417
418 if (drawing->modified)
419 {
420 GtkWidget *dialog;
421 gint result;
422 dialog = gtk_message_dialog_new(
423 GTK_WINDOW(drawing->top_level),
424 GTK_DIALOG_MODAL,
425 GTK_MESSAGE_WARNING,
426 GTK_BUTTONS_NONE,
427 _("Do you want to save the changes you made to \"%s\"?\nYour changes will be lost if you don't save them."),
428 drawing->filename->str);
429 #ifdef GTK_STOCK_DISCARD
430 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_DISCARD,GTK_RESPONSE_NO);
431 #else
432 gtk_dialog_add_button(GTK_DIALOG(dialog), _("Close _without Saving"), GTK_RESPONSE_NO);
433 #endif /* !GTK_STOCK_DISCARD */
434 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL);
435 gtk_dialog_add_button(GTK_DIALOG(dialog), GTK_STOCK_SAVE, GTK_RESPONSE_YES);
436
437 result = gtk_dialog_run(GTK_DIALOG(dialog));
438 gtk_widget_destroy(GTK_WIDGET(dialog));
439 while (gtk_events_pending())
440 {
441 gtk_main_iteration();
442 }
443
444 switch (result)
445 {
446 case GTK_RESPONSE_YES:
447 if (drawing_save(drawing))
448 {
449 cancel = FALSE;
450 }
451 else
452 {
453 cancel = TRUE;
454 }
455 break;
456 case GTK_RESPONSE_CANCEL:
457 cancel = TRUE;
458 break;
459 }
460 }
461 return cancel;
462 }
463
464 void
drawing_rotate(gpaint_drawing * drawing,double degrees)465 drawing_rotate(gpaint_drawing *drawing, double degrees)
466 {
467 gpaint_image *image = drawing_create_image(drawing);
468 if (image)
469 {
470 image_rotate(image, degrees);
471
472 /* copy rotated image on the pixmap */
473 gdk_pixmap_unref(drawing->backing_pixmap);
474 drawing_create_pixmap(drawing, image_width(image), image_height(image));
475 image_draw_on_pixmap(image, &drawing->backing_pixmap, drawing->gc);
476 image_free(image);
477 drawing_modified(drawing);
478 }
479 }
480
481
482
drawing_save_undo_pixmap(gpaint_drawing * drawing)483 inline void drawing_save_undo_pixmap(gpaint_drawing *drawing)
484 {
485 #if 0
486 debug("Backing up the backing pixmap\n");
487 gdk_draw_drawable(drawing->undo_pixmap,
488 drawing->gc,
489 drawing->backing_pixmap,
490 0, 0,
491 0, 0,
492 -1, -1);
493 #endif
494 }
495
drawing_undo(gpaint_drawing * drawing)496 inline void drawing_undo(gpaint_drawing *drawing)
497 {
498 #if 0
499 debug("Undoing the last backing pixmap operation\n");
500 gdk_draw_drawable(drawing->backing_pixmap,
501 drawing->gc,
502 drawing->undo_pixmap,
503 0, 0,
504 0, 0,
505 -1, -1);
506 #endif
507 }
508
509