]> git.donarmstrong.com Git - xournal.git/blob - src/xo-clipboard.c
more win32 bugfixes
[xournal.git] / src / xo-clipboard.c
1 /*
2  *  This program is free software; you can redistribute it and/or
3  *  modify it under the terms of the GNU General Public
4  *  License as published by the Free Software Foundation; either
5  *  version 2 of the License, or (at your option) any later version.
6  *
7  *  This software is distributed in the hope that it will be useful,
8  *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
9  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
10  *  General Public License for more details.
11  *
12  *  You should have received a copy of the GNU General Public License
13  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
14  */
15
16 #ifdef HAVE_CONFIG_H
17 #  include <config.h>
18 #endif
19
20 #include <string.h>
21 #include <gtk/gtk.h>
22
23 #include "xournal.h"
24 #include "xo-callbacks.h"
25 #include "xo-interface.h"
26 #include "xo-support.h"
27 #include "xo-misc.h"
28 #include "xo-paint.h"
29 #include "xo-image.h"
30
31 // the various formats in which we might present clipboard data
32 #define TARGET_XOURNAL 1
33 #define TARGET_TEXT    2
34 #define TARGET_PIXBUF  3
35 #define XOURNAL_TARGET_ATOM "_XOURNAL" 
36   /* change when serialized data format changes incompatibly */
37
38 typedef struct XojSelectionData {
39   int xo_data_len;
40   char *xo_data;
41   gchar *text_data;
42   GdkPixbuf *image_data;
43 } XojSelectionData;
44
45
46 void callback_clipboard_get(GtkClipboard *clipboard,
47                             GtkSelectionData *selection_data,
48                             guint info, gpointer user_data)
49 {
50   struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
51
52   switch (info) {
53     case TARGET_XOURNAL:
54       gtk_selection_data_set(selection_data,
55         gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 8, sel->xo_data, sel->xo_data_len);
56       break;
57     case TARGET_TEXT:
58       if (sel->text_data!=NULL) 
59         gtk_selection_data_set_text(selection_data, sel->text_data, -1);
60       break;
61     case TARGET_PIXBUF:
62       if (sel->image_data!=NULL)
63         gtk_selection_data_set_pixbuf(selection_data, sel->image_data);
64       break;
65   }
66 }
67
68 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
69 {
70   struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
71   
72   if (sel->xo_data!=NULL) g_free(sel->xo_data);
73   if (sel->text_data!=NULL) g_free(sel->text_data);
74   if (sel->image_data!=NULL) gdk_pixbuf_unref(sel->image_data);
75   g_free(sel);
76 }
77
78 void selection_to_clip(void)
79 {
80   struct XojSelectionData *sel;
81   int bufsz, nitems, val;
82   char *p;
83   GList *list;
84   struct Item *item;
85   GtkTargetList *targetlist;
86   GtkTargetEntry *targets;
87   int n_targets;
88   
89   if (ui.selection == NULL) return;
90   bufsz = 2*sizeof(int) // bufsz, nitems
91         + sizeof(struct BBox); // bbox
92   nitems = 0;
93   for (list = ui.selection->items; list != NULL; list = list->next) {
94     item = (struct Item *)list->data;
95     nitems++;
96     if (item->type == ITEM_STROKE) {
97       bufsz+= sizeof(int) // type
98             + sizeof(struct Brush) // brush
99             + sizeof(int) // num_points
100             + 2*item->path->num_points*sizeof(double); // the points
101       if (item->brush.variable_width)
102         bufsz += (item->path->num_points-1)*sizeof(double); // the widths
103     }
104     else if (item->type == ITEM_TEXT) {
105       bufsz+= sizeof(int) // type
106             + sizeof(struct Brush) // brush
107             + 2*sizeof(double) // bbox upper-left
108             + sizeof(int) // text len
109             + strlen(item->text)+1 // text
110             + sizeof(int) // font_name len
111             + strlen(item->font_name)+1 // font_name
112             + sizeof(double); // font_size
113     }
114     else if (item->type == ITEM_IMAGE) {
115       if (item->image_png == NULL) {
116         set_cursor_busy(TRUE);
117         if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL))
118           item->image_png_len = 0;       // failed for some reason, so forget it
119         set_cursor_busy(FALSE);
120       }
121       bufsz+= sizeof(int) // type
122         + sizeof(struct BBox)
123         + sizeof(gsize) // png_buflen
124         + item->image_png_len;
125     }
126     else bufsz+= sizeof(int); // type
127   }
128
129   // allocate selection data structure and buffer
130   sel = g_malloc(sizeof(struct XojSelectionData));
131   sel->xo_data_len = bufsz;
132   sel->xo_data = g_malloc(bufsz);
133   sel->image_data = NULL;
134   sel->text_data = NULL;
135
136   // fill in the data
137   p = sel->xo_data;
138   g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
139   g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
140   g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
141   for (list = ui.selection->items; list != NULL; list = list->next) {
142     item = (struct Item *)list->data;
143     g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
144     if (item->type == ITEM_STROKE) {
145       g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
146       g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
147       g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
148       p+= 2*item->path->num_points*sizeof(double);
149       if (item->brush.variable_width) {
150         g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
151         p+= (item->path->num_points-1)*sizeof(double);
152       }
153     }
154     if (item->type == ITEM_TEXT) {
155       g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
156       g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
157       g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
158       val = strlen(item->text);
159       g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
160       g_memmove(p, item->text, val+1); p+= val+1;
161       val = strlen(item->font_name);
162       g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
163       g_memmove(p, item->font_name, val+1); p+= val+1;
164       g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
165       if (nitems==1) sel->text_data = g_strdup(item->text); // single text item
166     }
167     if (item->type == ITEM_IMAGE) {
168       g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
169       g_memmove(p, &item->image_png_len, sizeof(gsize)); p+= sizeof(gsize);
170       if (item->image_png_len > 0) {
171         g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
172       }
173       if (nitems==1) sel->image_data = gdk_pixbuf_copy(item->image); // single image
174     }
175   }
176   
177   /* build list of valid targets */
178   targetlist = gtk_target_list_new(NULL, 0);
179   gtk_target_list_add(targetlist, 
180     gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 0, TARGET_XOURNAL);
181   if (sel->image_data!=NULL)
182     gtk_target_list_add_image_targets(targetlist, TARGET_PIXBUF, TRUE);
183   if (sel->text_data!=NULL) 
184     gtk_target_list_add_text_targets(targetlist, TARGET_TEXT);
185   targets = gtk_target_table_new_from_list(targetlist, &n_targets);
186   gtk_target_list_unref(targetlist);
187   
188   gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 
189        targets, n_targets,
190        callback_clipboard_get, callback_clipboard_clear, sel);
191   gtk_target_table_free(targets, n_targets);
192 }
193
194 // paste xournal native data
195 void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
196 {
197   unsigned char *p;
198   int nitems, npts, i, len;
199   struct Item *item;
200   double hoffset, voffset, cx, cy;
201   double *pf;
202   int sx, sy, wx, wy;
203   
204   reset_selection();
205   
206   ui.selection = g_new(struct Selection, 1);
207   p = sel_data->data + sizeof(int);
208   g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
209   ui.selection->type = ITEM_SELECTRECT;
210   ui.selection->layer = ui.cur_layer;
211   g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
212   ui.selection->items = NULL;
213   
214   // find by how much we translate the pasted selection
215   gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
216   gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
217   gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
218   cx -= ui.cur_page->hoffset;
219   cy -= ui.cur_page->voffset;
220   if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
221     cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
222   if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
223     cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
224   if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
225     cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
226   if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
227     cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
228   hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
229   voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
230   ui.selection->bbox.left += hoffset;
231   ui.selection->bbox.right += hoffset;
232   ui.selection->bbox.top += voffset;
233   ui.selection->bbox.bottom += voffset;
234
235   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
236       gnome_canvas_rect_get_type(), "width-pixels", 1,
237       "outline-color-rgba", 0x000000ff,
238       "fill-color-rgba", 0x80808040,
239       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right, 
240       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
241   make_dashed(ui.selection->canvas_item);
242
243   while (nitems-- > 0) {
244     item = g_new(struct Item, 1);
245     ui.selection->items = g_list_append(ui.selection->items, item);
246     ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
247     ui.cur_layer->nitems++;
248     g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
249     if (item->type == ITEM_STROKE) {
250       g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
251       g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
252       item->path = gnome_canvas_points_new(npts);
253       pf = (double *)p;
254       for (i=0; i<npts; i++) {
255         item->path->coords[2*i] = pf[2*i] + hoffset;
256         item->path->coords[2*i+1] = pf[2*i+1] + voffset;
257       }
258       p+= 2*item->path->num_points*sizeof(double);
259       if (item->brush.variable_width) {
260         item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
261         p+= (item->path->num_points-1)*sizeof(double);
262       }
263       else item->widths = NULL;
264       update_item_bbox(item);
265       make_canvas_item_one(ui.cur_layer->group, item);
266     }
267     if (item->type == ITEM_TEXT) {
268       g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
269       g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
270       g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
271       item->bbox.left += hoffset;
272       item->bbox.top += voffset;
273       g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
274       item->text = g_malloc(len+1);
275       g_memmove(item->text, p, len+1); p+= len+1;
276       g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
277       item->font_name = g_malloc(len+1);
278       g_memmove(item->font_name, p, len+1); p+= len+1;
279       g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
280       make_canvas_item_one(ui.cur_layer->group, item);
281     }
282     if (item->type == ITEM_IMAGE) {
283       item->canvas_item = NULL;
284       item->image_png = NULL;
285       item->image_png_len = 0;
286       g_memmove(&item->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
287       item->bbox.left += hoffset;
288       item->bbox.right += hoffset;
289       item->bbox.top += voffset;
290       item->bbox.bottom += voffset;
291       g_memmove(&item->image_png_len, p, sizeof(gsize)); p+= sizeof(gsize);
292       if (item->image_png_len > 0) {
293         item->image_png = g_memdup(p, item->image_png_len);
294         item->image = pixbuf_from_buffer(item->image_png, item->image_png_len);
295         p+= item->image_png_len;
296       } else {
297         item->image = NULL;
298       }
299       make_canvas_item_one(ui.cur_layer->group, item);
300     }
301   }
302
303   prepare_new_undo();
304   undo->type = ITEM_PASTE;
305   undo->layer = ui.cur_layer;
306   undo->itemlist = g_list_copy(ui.selection->items);  
307   
308   gtk_selection_data_free(sel_data);
309   update_copy_paste_enabled();
310   update_color_menu();
311   update_thickness_buttons();
312   update_color_buttons();
313   update_font_button();  
314   update_cursor(); // FIXME: can't know if pointer is within selection!
315 }
316
317 // paste external text
318 void clipboard_paste_text(gchar *text)
319 {
320   struct Item *item;
321   double pt[2];
322
323   reset_selection();
324   get_current_pointer_coords(pt);
325   set_current_page(pt);  
326
327   ui.selection = g_new(struct Selection, 1);
328   ui.selection->type = ITEM_SELECTRECT;
329   ui.selection->layer = ui.cur_layer;
330   ui.selection->items = NULL;
331
332   item = g_new(struct Item, 1);
333   ui.selection->items = g_list_append(ui.selection->items, item);
334   ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
335   ui.cur_layer->nitems++;
336   item->type = ITEM_TEXT;
337   g_memmove(&(item->brush), &(ui.brushes[ui.cur_mapping][TOOL_PEN]), sizeof(struct Brush));
338   item->text = text; // text was newly allocated, we keep it
339   item->font_name = g_strdup(ui.font_name);
340   item->font_size = ui.font_size;
341   item->bbox.left = pt[0]; item->bbox.top = pt[1];
342   make_canvas_item_one(ui.cur_layer->group, item);
343   update_item_bbox(item);
344
345   // move the text to fit on the page if needed
346   if (item->bbox.right > ui.cur_page->width) item->bbox.left += ui.cur_page->width-item->bbox.right;
347   if (item->bbox.left < 0) item->bbox.left = 0;
348   if (item->bbox.bottom > ui.cur_page->height) item->bbox.top += ui.cur_page->height-item->bbox.bottom;
349   if (item->bbox.top < 0) item->bbox.top = 0;
350   gnome_canvas_item_set(item->canvas_item, "x", item->bbox.left, "y", item->bbox.top, NULL);
351   update_item_bbox(item);
352   
353   ui.selection->bbox = item->bbox;
354   ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
355       gnome_canvas_rect_get_type(), "width-pixels", 1,
356       "outline-color-rgba", 0x000000ff,
357       "fill-color-rgba", 0x80808040,
358       "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right, 
359       "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
360   make_dashed(ui.selection->canvas_item);
361
362   prepare_new_undo();
363   undo->type = ITEM_PASTE;
364   undo->layer = ui.cur_layer;
365   undo->itemlist = g_list_copy(ui.selection->items);  
366   
367   update_copy_paste_enabled();
368   update_color_menu();
369   update_thickness_buttons();
370   update_color_buttons();
371   update_font_button();  
372   update_cursor(); // FIXME: can't know if pointer is within selection!
373 }
374
375 // paste an external image
376 void clipboard_paste_image(GdkPixbuf *pixbuf)
377 {
378   double pt[2];
379
380   reset_selection();
381
382   get_current_pointer_coords(pt);
383   set_current_page(pt);  
384
385   create_image_from_pixbuf(pixbuf, pt);
386 }
387
388 // work out what format the clipboard data is in, and paste accordingly
389 void clipboard_paste(void)
390 {
391   GtkSelectionData *sel_data;
392   GtkClipboard *clipboard;
393   GdkPixbuf *pixbuf;
394   gchar *text;
395
396   if (ui.cur_layer == NULL) return;
397   
398   ui.cur_item_type = ITEM_PASTE;
399   clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
400   // try xournal data
401   sel_data = gtk_clipboard_wait_for_contents(
402       clipboard,
403       gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE));
404 #ifdef WIN32 // avoid a win32 bug showing images as xournal data
405   if (gtk_selection_data_get_data_type(sel_data)!=gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE))
406     { gtk_selection_data_free(sel_data); sel_data = NULL; }
407 #endif
408   ui.cur_item_type = ITEM_NONE;
409   if (sel_data != NULL) { 
410     clipboard_paste_from_xournal(sel_data);
411     return;
412   } 
413   // try image data
414   pixbuf = gtk_clipboard_wait_for_image(clipboard);
415   if (pixbuf != NULL) {
416     clipboard_paste_image(pixbuf);
417     return;
418   }
419   // try text data
420   text = gtk_clipboard_wait_for_text(clipboard);
421   if (text != NULL) {
422     clipboard_paste_text(text);
423     return;
424   }
425 }