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