1 /* $Id: canvas.c,v 1.6 2005/01/07 02:50:52 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 #include <stdlib.h>
28 #include <string.h>
29 #include <gtk/gtk.h>
30 #include "debug.h"
31 #include "canvas.h"
32 #include "util.h"
33 #include "gtkscrollframe.h"
34 #include "image.h"
35 #include "image_processing.h"
36 #include "paste.h"
37 #include "selection.h"
38 #include "text.h"
39 #include "global.h"
40
41 #define GPAINT_CLIPBOARD_KEY "gpaint-clipboard"
42
43 /* Single clipboard to share selections between canvases. */
44 static gpaint_clipboard *clipboard = NULL;
45
46 /* macros to cast user_data in the callbacks */
47 #define CANVAS(user_data) ((gpaint_canvas*)(user_data))
48 #define ACTIVE_TOOL(user_data) (((gpaint_canvas*)(user_data))->active_tool)
49 #define DRAWING(user_data) (((gpaint_canvas*)(user_data))->drawing)
50
51 /* gobject canvas */
52 #define GPAINT_TYPE_CANVAS (gpaint_canvas_get_type ())
53 #define GPAINT_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GPAINT_TYPE_CANVAS, gpaint_canvas))
54 #define GPAINT_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GPAINT_TYPE_CANVAS, gpaint_canvas_class))
55 #define GPAINT_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GPAINT_TYPE_CANVAS))
56 #define GPAINT_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GPAINT_TYPE_CANVAS))
57 #define GPAINT_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), GPAINT_TYPE_CANVAS, gpaint_canvas_class))
58
59 GType gpaint_canvas_get_type (void);
60
61 /* File to open when starting. */
62 static gchar** canvas_initial_filenames = 0;
63
64 static gpaint_canvas* canvas_new(GtkDrawingArea *drawing_area);
65 static void canvas_copy_selection_to_clipboard(gpaint_canvas *canvas);
66
67 static void canvas_clipboard_free (gpaint_clipboard *clipboard);
68 static gint canvas_clipboard_format_compare (GdkPixbufFormat *a, GdkPixbufFormat *b);
69 static void canvas_clipboard_send_buffer(GtkClipboard *gtk_clipboard, GtkSelectionData *selection_data, guint info, gpaint_canvas *canvas);
70
71 static void
72 on_drawing_area_realize (GtkWidget *widget,
73 gpointer user_data);
74 static gboolean
75 on_drawing_area_expose_event (GtkWidget *widget,
76 GdkEventExpose *event,
77 gpointer user_data);
78 static gboolean
79 on_drawing_area_motion_notify_event (GtkWidget *widget,
80 GdkEventMotion *event,
81 gpointer user_data);
82 static gboolean
83 on_drawing_area_button_press_event (GtkWidget *widget,
84 GdkEventButton *event,
85 gpointer user_data);
86 static gboolean
87 on_drawing_area_button_release_event (GtkWidget *widget,
88 GdkEventButton *event,
89 gpointer user_data);
90 static gboolean
91 on_drawing_area_focus_in_event (GtkWidget *widget,
92 GdkEventFocus *event,
93 gpointer user_data);
94 static gboolean
95 on_drawing_area_focus_out_event (GtkWidget *widget,
96 GdkEventFocus *event,
97 gpointer user_data);
98 static gboolean
99 on_drawing_area_key_release_event (GtkWidget *widget,
100 GdkEventKey *event,
101 gpointer user_data);
102
103 /*
104 * Create the drawing area widget and place it in a scrolled view port. This
105 * function is called from ui.c when the main application window is created.
106 */
107 GtkWidget*
create_drawing_area_in_scroll_frame(gchar * widget_name,gchar * string1,gchar * string2,gint width,gint height)108 create_drawing_area_in_scroll_frame (
109 gchar *widget_name,
110 gchar *string1,
111 gchar *string2,
112 gint width,
113 gint height)
114 {
115 GtkWidget *scrolledwindow;
116 GtkWidget *viewport;
117 GtkWidget *drawing_area;
118
119 debug_fn();
120
121 /* Create the scroll frame, view port, and drawing area widgets. */
122 scrolledwindow = gtk_scroll_frame_new (NULL, NULL);
123 gtk_scroll_frame_set_policy(GTK_SCROLL_FRAME (scrolledwindow),
124 GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
125
126 viewport = gtk_viewport_new (NULL, NULL);
127 gtk_widget_set_name(viewport, "viewport");
128 gtk_widget_ref(viewport);
129 gtk_object_set_data_full(GTK_OBJECT (scrolledwindow), "viewport", viewport,
130 (GtkDestroyNotify) gtk_widget_unref);
131
132 gtk_widget_show(viewport);
133 gtk_container_add(GTK_CONTAINER (scrolledwindow), viewport);
134
135 drawing_area = gtk_drawing_area_new();
136 gtk_widget_set_name(drawing_area, DRAWING_AREA);
137 gtk_widget_ref(drawing_area);
138 gtk_object_set_data_full(GTK_OBJECT(scrolledwindow), DRAWING_AREA, drawing_area,
139 (GtkDestroyNotify) gtk_widget_unref);
140
141 gtk_container_add(GTK_CONTAINER(viewport), drawing_area);
142
143 gtk_widget_set_events(GTK_WIDGET(drawing_area),
144 GDK_EXPOSURE_MASK |
145 GDK_POINTER_MOTION_MASK |
146 GDK_POINTER_MOTION_HINT_MASK |
147 GDK_BUTTON_MOTION_MASK |
148 GDK_BUTTON1_MOTION_MASK |
149 GDK_BUTTON2_MOTION_MASK |
150 GDK_BUTTON3_MOTION_MASK |
151 GDK_BUTTON_PRESS_MASK |
152 GDK_BUTTON_RELEASE_MASK |
153 GDK_KEY_PRESS_MASK |
154 GDK_KEY_RELEASE_MASK |
155 GDK_PROPERTY_CHANGE_MASK |
156 GDK_VISIBILITY_NOTIFY_MASK |
157 GDK_FOCUS_CHANGE_MASK |
158 GDK_STRUCTURE_MASK |
159 GDK_ENTER_NOTIFY_MASK |
160 GDK_LEAVE_NOTIFY_MASK |
161 GDK_PROPERTY_CHANGE_MASK);
162 GTK_WIDGET_SET_FLAGS(drawing_area, GTK_CAN_FOCUS);
163 GTK_WIDGET_SET_FLAGS(drawing_area, GTK_CAN_DEFAULT);
164
165 /* connect the realize event to the function that will initialize
166 * the canvas object. */
167 gtk_signal_connect(GTK_OBJECT(drawing_area), "realize",
168 GTK_SIGNAL_FUNC(on_drawing_area_realize),
169 NULL);
170 gtk_widget_show(drawing_area);
171 return scrolledwindow;
172 }
173
174 /*
175 * Initialize the drawing area widget.
176 */
177 static void
on_drawing_area_realize(GtkWidget * drawing_area,gpointer user_data)178 on_drawing_area_realize (GtkWidget *drawing_area,
179 gpointer user_data)
180 {
181 /* Create the canvas object and initialize the window objects. */
182 gpaint_canvas *canvas = canvas_new(GTK_DRAWING_AREA(drawing_area));
183 g_assert(canvas);
184 debug_fn();
185
186 /* attach the application specific data object to the drawing area */
187 gtk_object_set_data_full(GTK_OBJECT(drawing_area),
188 "user_data",
189 canvas,
190 (GtkDestroyNotify)canvas_destroy);
191
192 gtk_drawing_area_size(GTK_DRAWING_AREA(drawing_area),
193 canvas->drawing->width,
194 canvas->drawing->height);
195
196 /* save a reference to the drawing area in the top_level window */
197 gtk_widget_ref(drawing_area);
198 gtk_object_set_data_full(GTK_OBJECT(canvas->top_level),
199 DRAWING_AREA,
200 drawing_area,
201 (GtkDestroyNotify)gtk_widget_unref);
202
203 /* Connect the event handlers and pass the canvas object to the
204 * callback functions. */
205 gtk_signal_connect(GTK_OBJECT(drawing_area), "button_press_event",
206 GTK_SIGNAL_FUNC(on_drawing_area_button_press_event),
207 canvas);
208 gtk_signal_connect(GTK_OBJECT(drawing_area), "button_release_event",
209 GTK_SIGNAL_FUNC(on_drawing_area_button_release_event),
210 canvas);
211 gtk_signal_connect(GTK_OBJECT (drawing_area), "motion_notify_event",
212 GTK_SIGNAL_FUNC(on_drawing_area_motion_notify_event),
213 canvas);
214 gtk_signal_connect(GTK_OBJECT(drawing_area), "expose_event",
215 GTK_SIGNAL_FUNC(on_drawing_area_expose_event),
216 canvas);
217 gtk_signal_connect(GTK_OBJECT(drawing_area), "focus_in_event",
218 GTK_SIGNAL_FUNC(on_drawing_area_focus_in_event),
219 canvas);
220 gtk_signal_connect(GTK_OBJECT(drawing_area), "focus_out_event",
221 GTK_SIGNAL_FUNC(on_drawing_area_focus_out_event),
222 canvas);
223 gtk_signal_connect(GTK_OBJECT(drawing_area), "key_release_event",
224 GTK_SIGNAL_FUNC(on_drawing_area_key_release_event),
225 canvas);
226 }
227
228 /*
229 * Copy the backing pixmap to the exposed area of the window.
230 */
231 static gboolean
on_drawing_area_expose_event(GtkWidget * widget,GdkEventExpose * event,gpointer user_data)232 on_drawing_area_expose_event (GtkWidget *widget,
233 GdkEventExpose *event,
234 gpointer user_data)
235 {
236 gpaint_canvas *canvas = (gpaint_canvas*)user_data;
237 gpaint_tool *tool = ACTIVE_TOOL(user_data);
238 debug_fn();
239 gdk_draw_pixmap(widget->window,
240 widget->style->fg_gc[GTK_WIDGET_STATE(widget)],
241 canvas->drawing->backing_pixmap,
242 event->area.x, event->area.y,
243 event->area.x, event->area.y,
244 event->area.width, event->area.height);
245 if (tool && tool->current_draw) tool->current_draw(tool);
246 return FALSE;
247 }
248
249 /*
250 * Dispatch the motion event to the current tool object. The motion
251 * hint mask is used to collapse motion events. This prevents the
252 * the application from falling behind if events are dispatched by
253 * the X server faster than the application can process them.
254 */
255 static gboolean
on_drawing_area_motion_notify_event(GtkWidget * widget,GdkEventMotion * event,gpointer user_data)256 on_drawing_area_motion_notify_event (GtkWidget *widget,
257 GdkEventMotion *event,
258 gpointer user_data)
259 {
260 gpaint_tool *tool = ACTIVE_TOOL(user_data);
261 int x, y;
262 GdkModifierType state;
263
264 if (tool && tool->motion)
265 {
266 if (event->is_hint)
267 {
268 gdk_window_get_pointer (event->window, &x, &y, &state);
269 }
270 else
271 {
272 x = event->x;
273 y = event->y;
274 state = event->state;
275 }
276
277 if (state & GDK_BUTTON1_MASK)
278 {
279 (*tool->motion)(tool, x, y);
280 }
281 }
282 return TRUE;
283 }
284
285 /*
286 * Dispatch the mouse button press to the active tool.
287 */
288 static gboolean
on_drawing_area_button_press_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)289 on_drawing_area_button_press_event (GtkWidget *widget,
290 GdkEventButton *event,
291 gpointer user_data)
292 {
293 gpaint_tool *tool = ACTIVE_TOOL(user_data);
294 if (event->button==1 && tool && tool->button_press)
295 {
296 (*tool->button_press)(tool, event->x, event->y);
297 }
298 return FALSE;
299 }
300
301 /*
302 * Dispatch the mouse button release to the active tool.
303 */
304 static gboolean
on_drawing_area_button_release_event(GtkWidget * widget,GdkEventButton * event,gpointer user_data)305 on_drawing_area_button_release_event (GtkWidget *widget,
306 GdkEventButton *event,
307 gpointer user_data)
308 {
309 gpaint_tool *tool = ACTIVE_TOOL(user_data);
310 if (event->button==1 && tool && tool->button_release)
311 {
312 (*tool->button_release)(tool, event->x, event->y);
313 }
314 return FALSE;
315 }
316
317 /*
318 * Dispatch the focus in event to the active tool.
319 */
320 static gboolean
on_drawing_area_focus_in_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)321 on_drawing_area_focus_in_event (GtkWidget *widget,
322 GdkEventFocus *event,
323 gpointer user_data)
324 {
325 gpaint_canvas *canvas = CANVAS(user_data);
326 debug_fn1("canvas=%p", canvas);
327 canvas_focus_gained(canvas);
328 return FALSE;
329 }
330
331 /*
332 * Dispatch the focus out event to the active tool.
333 */
334 static gboolean
on_drawing_area_focus_out_event(GtkWidget * widget,GdkEventFocus * event,gpointer user_data)335 on_drawing_area_focus_out_event (GtkWidget *widget,
336 GdkEventFocus *event,
337 gpointer user_data)
338 {
339 gpaint_canvas *canvas = CANVAS(user_data);
340 debug_fn1("canvas=%p", canvas);
341 canvas_focus_lost(canvas);
342 return FALSE;
343 }
344
345 /*
346 * Dispatch the key release to the active tool.
347 */
348 static gboolean
on_drawing_area_key_release_event(GtkWidget * widget,GdkEventKey * event,gpointer user_data)349 on_drawing_area_key_release_event (GtkWidget *widget,
350 GdkEventKey *event,
351 gpointer user_data)
352 {
353 gpaint_tool *tool = ACTIVE_TOOL(user_data);
354 if (tool && tool->key_release)
355 {
356 (*tool->key_release)(tool, event);
357 }
358 return FALSE;
359 }
360
361 /*
362 * Save the application command line arguments. The first
363 * argument is the filename to be initially opened. A
364 * blank drawing is created if no file name is given.
365 */
366 void
canvas_init_arg(int argc,char * argv[])367 canvas_init_arg(int argc, char* argv[])
368 {
369 int i;
370 if (argc > 1)
371 {
372 canvas_initial_filenames = calloc(sizeof(char*), argc - 1);
373 for (i = 1; i < argc; i++)
374 canvas_initial_filenames[i - 1] = argv[i];
375 }
376 return;
377 }
378
379
380 /*
381 * Lookup the canvas object from a widget.
382 */
383 gpaint_canvas*
canvas_lookup(GtkWidget * widget)384 canvas_lookup(GtkWidget *widget)
385 {
386 GtkWidget *cw;
387 gpaint_canvas *canvas;
388 cw = lookup_widget(widget, DRAWING_AREA);
389 g_assert(cw) ;
390 canvas = (gpaint_canvas*)gtk_object_get_data(GTK_OBJECT(cw), "user_data");
391 g_assert(canvas);
392 return canvas;
393 }
394
395 /*
396 * Create a new canvas object. The canvas object is created when the drawing
397 * area window is realized.
398 */
399 gpaint_canvas *
canvas_new(GtkDrawingArea * drawing_area)400 canvas_new(GtkDrawingArea *drawing_area)
401 {
402 gpaint_canvas *canvas = 0;
403 gpaint_drawing *drawing = 0;
404 GdkDrawable *d = GTK_WIDGET(drawing_area)->window;
405 GdkGC *gc;
406 GdkColor black;
407 GdkColor white;
408
409 /* Create the gc from the drawing area window. */
410 gc = gdk_gc_new(d);
411 g_assert(gc);
412 gdk_gc_ref(gc);
413
414 gdk_color_black(gdk_colormap_get_system(), &black);
415 gdk_color_white(gdk_colormap_get_system(), &white);
416 gdk_gc_set_foreground(gc, &black);
417 gdk_gc_set_background(gc, &white);
418 gdk_gc_set_line_attributes(gc, 1, GDK_LINE_SOLID, GDK_CAP_ROUND, GDK_JOIN_ROUND);
419
420 /* Create the canvas object */
421 // canvas = (gpaint_canvas*)g_new0(gpaint_canvas, 1);
422 canvas = g_object_new (GPAINT_TYPE_CANVAS, NULL);
423 g_assert(canvas);
424 canvas->top_level = gtk_widget_get_toplevel(GTK_WIDGET(drawing_area));
425 canvas->drawing_area = drawing_area;
426 canvas->gc = gc;
427 canvas->busy_cursor = gdk_cursor_new(GDK_WATCH);
428 canvas->arrow_cursor = gdk_cursor_new(GDK_LEFT_PTR);
429 canvas->cursor = canvas->arrow_cursor;
430 canvas->active_tool = 0;
431 canvas->saved_tool = 0;
432 canvas->paste_tool = paste_create("paste");
433 canvas->selection = selection_new(drawing_area);
434
435 /* Create the drawing object */
436 if (canvas_initial_filenames)
437 { /* only open the first one for now; open additonal ones TO DO */
438
439 drawing = drawing_new_from_file(canvas->drawing_area, canvas->gc, canvas_initial_filenames[0]);
440 if (!drawing)
441 {
442 g_message("Failed to open drawing file %s", canvas_initial_filenames[0]);
443 }
444 free(canvas_initial_filenames);
445 canvas_initial_filenames = 0; /* use only in the first window */
446 }
447 if (!drawing)
448 {
449 drawing = drawing_new_blank(canvas->drawing_area, canvas->gc, DEFAULT_WIDTH, DEFAULT_HEIGHT);
450 }
451 g_assert(drawing);
452 canvas->drawing = drawing;
453 selection_size(canvas->selection, drawing->width, drawing->height);
454
455 return canvas;
456 }
457
458 GType
gpaint_canvas_get_type(void)459 gpaint_canvas_get_type (void)
460 {
461 static GType object_type = 0;
462
463 if (!object_type)
464 {
465 static const GTypeInfo object_info =
466 {
467 sizeof (gpaint_canvas_class),
468 (GBaseInitFunc) NULL,
469 (GBaseFinalizeFunc) NULL,
470 (GClassInitFunc) NULL,
471 NULL, /* class_finalize */
472 NULL, /* class_data */
473 sizeof (gpaint_canvas),
474 0,
475 (GInstanceInitFunc) NULL,
476 };
477
478 object_type = g_type_register_static (G_TYPE_OBJECT,
479 "GpaintCanvas",
480 &object_info, 0);
481 }
482
483 return object_type;
484 }
485
486 void
canvas_destroy(gpaint_canvas * canvas)487 canvas_destroy(gpaint_canvas *canvas)
488 {
489 debug_fn();
490 g_assert(canvas);
491
492 if (canvas->paste_tool)
493 {
494 gpaint_tool *tool = canvas->paste_tool;
495 g_assert(tool->destroy);
496 (*tool->destroy)(tool);
497 }
498
499 drawing_destroy(canvas->drawing);
500 selection_delete(canvas->selection);
501 gdk_gc_unref(canvas->gc);
502 gdk_cursor_destroy(canvas->arrow_cursor);
503 gdk_cursor_destroy(canvas->busy_cursor);
504 // memset(canvas, 0xBEBE, sizeof(canvas)); /* debugging aid */
505 // g_free(canvas);
506 g_object_unref(canvas);
507 debug("canvas_destroy() returning");
508 }
509
510 /*
511 * Display the new drawing and destroy the current drawing.
512 */
513 void
canvas_set_drawing(gpaint_canvas * canvas,gpaint_drawing * new_drawing)514 canvas_set_drawing(gpaint_canvas *canvas, gpaint_drawing *new_drawing)
515 {
516 gpaint_drawing *old_drawing;
517 g_assert(canvas);
518 g_assert(new_drawing);
519
520 old_drawing = canvas->drawing;
521
522 canvas->drawing = new_drawing;
523 selection_remove_points(canvas->selection);
524 if (canvas->active_tool)
525 {
526 canvas->active_tool->drawing = canvas->drawing;
527 }
528 gtk_drawing_area_size(
529 GTK_DRAWING_AREA(canvas->drawing_area),
530 canvas->drawing->width,
531 canvas->drawing->height);
532 selection_size(canvas->selection, canvas->drawing->width, canvas->drawing->height);
533 drawing_destroy(old_drawing);
534 canvas_redraw(canvas);
535 }
536
537 /*
538 * Resize the canvas to the current drawing size. Should be called
539 * when the drawing size has been changed.
540 */
541 void
canvas_resize(gpaint_canvas * canvas)542 canvas_resize(gpaint_canvas *canvas)
543 {
544 selection_remove_points(canvas->selection);
545 selection_size(canvas->selection, canvas->drawing->width, canvas->drawing->height);
546 gtk_drawing_area_size(
547 GTK_DRAWING_AREA(canvas->drawing_area),
548 canvas->drawing->width,
549 canvas->drawing->height);
550 canvas_redraw(canvas);
551 }
552
553
554 /*
555 * Set the active drawing tool object.
556 */
557 void
canvas_set_tool(gpaint_canvas * canvas,gpaint_tool * new_tool)558 canvas_set_tool(gpaint_canvas* canvas, gpaint_tool *new_tool)
559 {
560 gpaint_tool *old_tool = canvas->active_tool;
561
562 if (old_tool && old_tool->deselect)
563 {
564 (*old_tool->deselect)(old_tool);
565 }
566
567 canvas->active_tool = new_tool;
568 if (!canvas->active_tool) /* null means remove active tool */
569 {
570 canvas->cursor = canvas->arrow_cursor;
571 }
572 else
573 {
574 new_tool->drawing = canvas->drawing;
575 new_tool->canvas = canvas;
576 if (new_tool->select)
577 {
578 (*new_tool->select)(new_tool);
579 }
580 canvas->cursor = new_tool->cursor;
581 }
582 gdk_window_set_cursor(canvas->drawing->window, canvas->cursor);
583 }
584
585 void
canvas_begin_busy_cursor(gpaint_canvas * canvas)586 canvas_begin_busy_cursor(gpaint_canvas *canvas)
587 {
588 canvas_commit_change(canvas);
589 gdk_window_set_cursor(canvas->top_level->window, canvas->busy_cursor);
590 gdk_window_set_cursor(canvas->drawing->window, canvas->busy_cursor);
591 gdk_flush();
592 }
593
594 void
canvas_end_busy_cursor(gpaint_canvas * canvas)595 canvas_end_busy_cursor(gpaint_canvas *canvas)
596 {
597 gdk_window_set_cursor(canvas->top_level->window, canvas->arrow_cursor);
598 if (canvas->cursor)
599 {
600 gdk_window_set_cursor(canvas->drawing->window, canvas->cursor);
601 }
602 else
603 {
604 gdk_window_set_cursor(canvas->drawing->window, canvas->arrow_cursor);
605 }
606 gdk_flush();
607 }
608
609 void
canvas_focus_gained(gpaint_canvas * canvas)610 canvas_focus_gained(gpaint_canvas *canvas)
611 {
612 gpaint_tool *tool = canvas->active_tool;
613 debug_fn1("canvas=%p", canvas);
614
615 selection_enable_flash(canvas->selection);
616 canvas->has_focus = TRUE;
617 if (tool && tool->change)
618 {
619 (*tool->change)(tool, GpaintFocusIn, NULL);
620 }
621 }
622
623 void
canvas_focus_lost(gpaint_canvas * canvas)624 canvas_focus_lost(gpaint_canvas *canvas)
625 {
626 gpaint_tool *tool = canvas->active_tool;
627 debug_fn1("canvas=%p", canvas);
628
629 selection_disable_flash(canvas->selection);
630 canvas->has_focus = FALSE;
631 if (tool && tool->change)
632 {
633 (*tool->change)(tool, GpaintFocusOut, NULL);
634 }
635 }
636
637 void
canvas_redraw(gpaint_canvas * canvas)638 canvas_redraw(gpaint_canvas *canvas)
639 {
640 GtkWidget *widget = GTK_WIDGET(canvas->drawing_area);
641 int width;
642 int height;
643
644 gdk_window_get_size(widget->window, &width, &height);
645 gtk_widget_queue_draw_area(widget, 0, 0, width, height);
646 gtk_widget_queue_draw(widget);
647 }
648
649 void
canvas_begin_paste_mode(gpaint_canvas * canvas)650 canvas_begin_paste_mode(gpaint_canvas *canvas)
651 {
652 gpaint_clipboard *clipboard = canvas_clipboard(canvas);
653 canvas_commit_change(canvas);
654 // if (clipboard && point_array_size(clipboard->points))
655 {
656 if (canvas->active_tool != canvas->paste_tool)
657 {
658 canvas->saved_tool = canvas->active_tool;
659 canvas_set_tool(canvas, canvas->paste_tool);
660 }
661 }
662 }
663
664 void
canvas_end_paste_mode(gpaint_canvas * canvas)665 canvas_end_paste_mode(gpaint_canvas *canvas)
666 {
667 if (canvas->active_tool == canvas->paste_tool)
668 {
669 canvas_set_tool(canvas, canvas->saved_tool);
670 }
671 }
672
673 void
canvas_cut(gpaint_canvas * canvas)674 canvas_cut(gpaint_canvas *canvas)
675 {
676 canvas_commit_change(canvas);
677 if (canvas_has_selection(canvas))
678 {
679 selection_disable_flash(canvas->selection);
680 canvas_copy_selection_to_clipboard(canvas);
681 drawing_clear_selection(canvas->drawing, selection_points(canvas->selection));
682 selection_enable_flash(canvas->selection);
683 }
684 }
685
686 void
canvas_copy(gpaint_canvas * canvas)687 canvas_copy(gpaint_canvas *canvas)
688 {
689 canvas_commit_change(canvas);
690 if (canvas_has_selection(canvas))
691 {
692 selection_disable_flash(canvas->selection);
693 canvas_copy_selection_to_clipboard(canvas);
694 selection_enable_flash(canvas->selection);
695 }
696 }
697
698 void
canvas_clear(gpaint_canvas * canvas)699 canvas_clear(gpaint_canvas *canvas)
700 {
701
702 if (canvas_has_selection(canvas))
703 {
704 selection_disable_flash(canvas->selection);
705 drawing_clear_selection(canvas->drawing, selection_points(canvas->selection));
706 selection_enable_flash(canvas->selection);
707 }
708 else
709 {
710 drawing_clear(canvas->drawing);
711 canvas_redraw(canvas);
712 }
713 }
714
715 void
canvas_select_all(gpaint_canvas * canvas)716 canvas_select_all(gpaint_canvas *canvas)
717 {
718 GdkRectangle rect;
719 canvas_commit_change(canvas);
720 rect.x = 0;
721 rect.y = 0;
722 rect.width = canvas->drawing->width;
723 rect.height = canvas->drawing->height;
724 selection_set_rectangle(canvas->selection, rect);
725 }
726
727 gboolean
canvas_has_selection(gpaint_canvas * canvas)728 canvas_has_selection(gpaint_canvas *canvas)
729 {
730 return (selection_num_points(canvas->selection) > 2);
731 }
732
733 gpaint_clipboard *
canvas_clipboard(gpaint_canvas * canvas)734 canvas_clipboard(gpaint_canvas *canvas)
735 {
736 /* create the single clipboard */
737 if (!clipboard)
738 {
739 GSList *list;
740
741 clipboard = g_new0(gpaint_clipboard, 1);
742 clipboard->image = 0;
743 clipboard->points = point_array_new();
744
745 /* create gobject for clipboard notifications */
746 g_object_set_data_full (G_OBJECT (canvas), GPAINT_CLIPBOARD_KEY,
747 clipboard, (GDestroyNotify) canvas_clipboard_free);
748
749 /* create list of pixbuf formats for clipboard format negotiation */
750
751 clipboard->pixbuf_formats =
752 g_slist_sort (gdk_pixbuf_get_formats(),
753 (GCompareFunc) canvas_clipboard_format_compare);
754 for (list = clipboard->pixbuf_formats; list; list = g_slist_next (list))
755 {
756 GdkPixbufFormat *format = list->data;
757
758 if (gdk_pixbuf_format_is_writable (format))
759 {
760 gchar **mime_types;
761 gchar **type;
762
763 mime_types = gdk_pixbuf_format_get_mime_types (format);
764
765 for (type = mime_types; *type; type++)
766 clipboard->n_target_entries++;
767
768 g_strfreev (mime_types);
769 }
770 }
771
772 if (clipboard->n_target_entries > 0)
773 {
774 gint i = 0;
775
776 clipboard->target_entries = g_new0(GtkTargetEntry,
777 clipboard->n_target_entries);
778 clipboard->savers = g_new0(gchar*,
779 clipboard->n_target_entries + 1);
780
781 for (list = clipboard->pixbuf_formats; list; list = g_slist_next (list))
782 {
783 GdkPixbufFormat *format = list->data;
784
785 if (gdk_pixbuf_format_is_writable (format))
786 {
787 gchar *format_name;
788 gchar **mime_types;
789 gchar **type;
790
791 format_name = gdk_pixbuf_format_get_name (format);
792 mime_types = gdk_pixbuf_format_get_mime_types (format);
793
794 for (type = mime_types; *type; type++)
795 {
796 gchar *mime_type = *type;
797
798 clipboard->target_entries[i].target = g_strdup (mime_type);
799 clipboard->target_entries[i].flags = 0;
800 clipboard->target_entries[i].info = i;
801
802 clipboard->savers[i] = g_strdup (format_name);
803
804 i++;
805 }
806
807 g_strfreev (mime_types);
808 g_free (format_name);
809 }
810 }
811 }
812 }
813 return clipboard;
814 }
815
816 static void
canvas_clipboard_free(gpaint_clipboard * clipboard)817 canvas_clipboard_free (gpaint_clipboard *clipboard)
818 {
819 g_slist_free (clipboard->pixbuf_formats);
820 g_free (clipboard->target_entries);
821 g_strfreev (clipboard->savers);
822 g_free (clipboard);
823 }
824
825 static void
canvas_copy_selection_to_clipboard(gpaint_canvas * canvas)826 canvas_copy_selection_to_clipboard(gpaint_canvas *canvas)
827 {
828 gpaint_clipboard *clipboard = canvas_clipboard(canvas);
829 GtkClipboard *gtk_clipboard;
830
831 if (clipboard->image)
832 {
833 image_free(clipboard->image);
834 }
835 clipboard->image = image_from_selection(
836 canvas->drawing->backing_pixmap,
837 selection_points(canvas->selection));
838 point_array_copy(clipboard->points, selection_points(canvas->selection));
839
840 gtk_clipboard = gtk_clipboard_get_for_display (gdk_display_get_default(),
841 GDK_SELECTION_CLIPBOARD);
842 if (!gtk_clipboard)
843 return;
844
845 if (clipboard->image)
846 {
847 gtk_clipboard_set_with_owner (gtk_clipboard,
848 clipboard->target_entries,
849 clipboard->n_target_entries,
850 (GtkClipboardGetFunc) canvas_clipboard_send_buffer,
851 (GtkClipboardClearFunc) NULL,
852 G_OBJECT (canvas));
853 }
854 else if (gtk_clipboard_get_owner (gtk_clipboard) == G_OBJECT(canvas))
855 {
856 gtk_clipboard_clear (gtk_clipboard);
857 }
858 }
859
860 static GdkPixbuf*
clipboard_pixbuf(gpaint_clipboard * clipboard)861 clipboard_pixbuf(gpaint_clipboard *clipboard) {
862 return image_pixbuf(clipboard->image);
863 }
864
865
866 static void
canvas_clipboard_send_buffer(GtkClipboard * gtk_clipboard,GtkSelectionData * selection_data,guint info,gpaint_canvas * canvas)867 canvas_clipboard_send_buffer(GtkClipboard *gtk_clipboard,
868 GtkSelectionData *selection_data,
869 guint info,
870 gpaint_canvas *canvas)
871 {
872 gpaint_clipboard *clipboard = canvas_clipboard(canvas);
873 GdkPixbuf *pixbuf = clipboard_pixbuf(clipboard);
874
875 if (pixbuf)
876 {
877 GdkAtom atom = gdk_atom_intern (clipboard->target_entries[info].target,
878 FALSE);
879
880 g_print ("sending pixbuf data as '%s' (%s)\n",
881 clipboard->target_entries[info].target,
882 clipboard->savers[info]);
883 gchar *buffer;
884 gsize buffer_size;
885 GError *error = NULL;
886
887 g_return_if_fail (selection_data != NULL);
888 g_return_if_fail (atom != GDK_NONE);
889 g_return_if_fail (GDK_IS_PIXBUF (pixbuf));
890 g_return_if_fail (clipboard->savers[info] != NULL);
891
892 if (gdk_pixbuf_save_to_buffer (pixbuf,
893 &buffer, &buffer_size, clipboard->savers[info],
894 &error, NULL))
895 {
896 gtk_selection_data_set (selection_data, atom,
897 8, (guchar*) buffer, buffer_size);
898 }
899 }
900 }
901
902 static gint
canvas_clipboard_format_compare(GdkPixbufFormat * a,GdkPixbufFormat * b)903 canvas_clipboard_format_compare (GdkPixbufFormat *a,
904 GdkPixbufFormat *b)
905 {
906 gchar *a_name = gdk_pixbuf_format_get_name (a);
907 gchar *b_name = gdk_pixbuf_format_get_name (b);
908 gint retval = 0;
909
910 #ifdef GDK_WINDOWING_WIN32
911 /* move BMP to the front of the list */
912 if (g_ascii_strncasecmp (a_name, "bmp", strlen("bmp")) == 0)
913 retval = -1;
914 else if (g_ascii_strncasecmp (b_name, "bmp", strlen("bmp")) == 0)
915 retval = 1;
916 else
917 #endif
918
919 /* move PNG to the front of the list */
920 if (g_ascii_strncasecmp (a_name, "png", strlen("png")) == 0)
921 retval = -1;
922 else if (g_ascii_strncasecmp (b_name, "png", strlen("png")) == 0)
923 retval = 1;
924
925 /* move JPEG to the end of the list */
926 else if (g_ascii_strncasecmp (a_name, "jpeg", strlen("jpeg")) == 0)
927 retval = 1;
928 else if (g_ascii_strncasecmp (b_name, "jpeg", strlen("jpeg")) == 0)
929 retval = -1;
930
931 /* move GIF to the end of the list */
932 else if (g_ascii_strncasecmp (a_name, "gif", strlen("gif")) == 0)
933 retval = 1;
934 else if (g_ascii_strncasecmp (b_name, "gif", strlen("gif")) == 0)
935 retval = -1;
936
937 g_free (a_name);
938 g_free (b_name);
939
940 return retval;
941 }
942
canvas_commit_change(gpaint_canvas * canvas)943 void canvas_commit_change(gpaint_canvas *canvas)
944 {
945 gpaint_tool * tool = canvas->active_tool;
946 if (tool && tool->commit_change)
947 tool->commit_change(tool);
948 }
949