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.
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.
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/>.
24 #include "xo-callbacks.h"
25 #include "xo-interface.h"
26 #include "xo-support.h"
31 // the various formats in which we might present clipboard data
32 #define TARGET_XOURNAL 1
34 #define TARGET_PIXBUF 3
35 #define XOURNAL_TARGET_ATOM "_XOURNAL"
36 /* change when serialized data format changes incompatibly */
38 typedef struct XojSelectionData {
42 GdkPixbuf *image_data;
46 void callback_clipboard_get(GtkClipboard *clipboard,
47 GtkSelectionData *selection_data,
48 guint info, gpointer user_data)
50 struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
54 gtk_selection_data_set(selection_data,
55 gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 8, sel->xo_data, sel->xo_data_len);
58 if (sel->text_data!=NULL)
59 gtk_selection_data_set_text(selection_data, sel->text_data, -1);
62 if (sel->image_data!=NULL)
63 gtk_selection_data_set_pixbuf(selection_data, sel->image_data);
68 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
70 struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
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);
78 void selection_to_clip(void)
80 struct XojSelectionData *sel;
81 int bufsz, nitems, val;
85 GtkTargetList *targetlist;
86 GtkTargetEntry *targets;
89 if (ui.selection == NULL) return;
90 bufsz = 2*sizeof(int) // bufsz, nitems
91 + sizeof(struct BBox); // bbox
93 for (list = ui.selection->items; list != NULL; list = list->next) {
94 item = (struct Item *)list->data;
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
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
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);
121 bufsz+= sizeof(int) // type
122 + sizeof(struct BBox)
123 + sizeof(gsize) // png_buflen
124 + item->image_png_len;
126 else bufsz+= sizeof(int); // type
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;
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);
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
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;
173 if (nitems==1) sel->image_data = gdk_pixbuf_copy(item->image); // single image
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);
188 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
190 callback_clipboard_get, callback_clipboard_clear, sel);
191 gtk_target_table_free(targets, n_targets);
194 // paste xournal native data
195 void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
198 int nitems, npts, i, len;
200 double hoffset, voffset, cx, cy;
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;
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;
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);
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);
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;
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);
263 else item->widths = NULL;
264 update_item_bbox(item);
265 make_canvas_item_one(ui.cur_layer->group, item);
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);
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;
299 make_canvas_item_one(ui.cur_layer->group, item);
304 undo->type = ITEM_PASTE;
305 undo->layer = ui.cur_layer;
306 undo->itemlist = g_list_copy(ui.selection->items);
308 gtk_selection_data_free(sel_data);
309 update_copy_paste_enabled();
311 update_thickness_buttons();
312 update_color_buttons();
313 update_font_button();
314 update_cursor(); // FIXME: can't know if pointer is within selection!
317 // paste external text
318 void clipboard_paste_text(gchar *text)
324 get_current_pointer_coords(pt);
325 set_current_page(pt);
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;
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);
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);
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);
363 undo->type = ITEM_PASTE;
364 undo->layer = ui.cur_layer;
365 undo->itemlist = g_list_copy(ui.selection->items);
367 update_copy_paste_enabled();
369 update_thickness_buttons();
370 update_color_buttons();
371 update_font_button();
372 update_cursor(); // FIXME: can't know if pointer is within selection!
375 // paste an external image
376 void clipboard_paste_image(GdkPixbuf *pixbuf)
382 get_current_pointer_coords(pt);
383 set_current_page(pt);
385 create_image_from_pixbuf(pixbuf, pt);
388 // work out what format the clipboard data is in, and paste accordingly
389 void clipboard_paste(void)
391 GtkSelectionData *sel_data;
392 GtkClipboard *clipboard;
396 if (ui.cur_layer == NULL) return;
398 ui.cur_item_type = ITEM_PASTE;
399 clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
401 sel_data = gtk_clipboard_wait_for_contents(
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; }
408 ui.cur_item_type = ITEM_NONE;
409 if (sel_data != NULL) {
410 clipboard_paste_from_xournal(sel_data);
414 pixbuf = gtk_clipboard_wait_for_image(clipboard);
415 if (pixbuf != NULL) {
416 clipboard_paste_image(pixbuf);
420 text = gtk_clipboard_wait_for_text(clipboard);
422 clipboard_paste_text(text);