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 #define TARGET_XOURNAL 1
33 #define TARGET_PIXBUF 3
34 #define XOURNAL_TARGET_ATOM "_XOURNAL"
35 /* change when serialized data format changes incompatibly */
37 void callback_clipboard_get(GtkClipboard *clipboard,
38 GtkSelectionData *selection_data,
39 guint info, gpointer user_data)
46 g_memmove(&length, user_data, sizeof(int));
47 p = ((char *)user_data) + length;
48 g_memmove(&text, p, sizeof(gchar *)); p+= sizeof(gchar *);
49 g_memmove(&pixbuf, p, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
53 gtk_selection_data_set(selection_data,
54 gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 8, user_data, length);
58 gtk_selection_data_set_text(selection_data, text, -1);
62 gtk_selection_data_set_pixbuf(selection_data, pixbuf);
67 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
74 g_memmove(&length, user_data, sizeof(int));
75 p = ((char *)user_data) + length;
76 g_memmove(&text, p, sizeof(gchar *)); p+= sizeof(gchar *);
77 g_memmove(&pixbuf, p, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
79 if (text!=NULL) g_free(text);
80 if (pixbuf!=NULL) gdk_pixbuf_unref(pixbuf);
84 void selection_to_clip(void)
86 int bufsz, nitems, val;
90 GtkTargetList *targetlist;
91 GtkTargetEntry *targets;
92 GdkPixbuf *target_pixbuf;
96 if (ui.selection == NULL) return;
97 bufsz = 2*sizeof(int) // bufsz, nitems
98 + sizeof(struct BBox); // bbox
100 for (list = ui.selection->items; list != NULL; list = list->next) {
101 item = (struct Item *)list->data;
103 if (item->type == ITEM_STROKE) {
104 bufsz+= sizeof(int) // type
105 + sizeof(struct Brush) // brush
106 + sizeof(int) // num_points
107 + 2*item->path->num_points*sizeof(double); // the points
108 if (item->brush.variable_width)
109 bufsz += (item->path->num_points-1)*sizeof(double); // the widths
111 else if (item->type == ITEM_TEXT) {
112 bufsz+= sizeof(int) // type
113 + sizeof(struct Brush) // brush
114 + 2*sizeof(double) // bbox upper-left
115 + sizeof(int) // text len
116 + strlen(item->text)+1 // text
117 + sizeof(int) // font_name len
118 + strlen(item->font_name)+1 // font_name
119 + sizeof(double); // font_size
121 else if (item->type == ITEM_IMAGE) {
122 if (item->image_png == NULL) {
123 set_cursor_busy(TRUE);
124 if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL))
125 item->image_png_len = 0; // failed for some reason, so forget it
126 set_cursor_busy(FALSE);
128 bufsz+= sizeof(int) // type
129 + sizeof(struct BBox)
130 + sizeof(gsize) // png_buflen
131 + item->image_png_len;
133 else bufsz+= sizeof(int); // type
135 // allocate buffer; allow space for alternative target representations at the end
136 p = buf = g_malloc(bufsz + sizeof(gchar *) + sizeof(GdkPixbuf *));
137 target_pixbuf = NULL;
139 g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
140 g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
141 g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
142 for (list = ui.selection->items; list != NULL; list = list->next) {
143 item = (struct Item *)list->data;
144 g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
145 if (item->type == ITEM_STROKE) {
146 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
147 g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
148 g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
149 p+= 2*item->path->num_points*sizeof(double);
150 if (item->brush.variable_width) {
151 g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
152 p+= (item->path->num_points-1)*sizeof(double);
155 if (item->type == ITEM_TEXT) {
156 g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
157 g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
158 g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
159 val = strlen(item->text);
160 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
161 g_memmove(p, item->text, val+1); p+= val+1;
162 val = strlen(item->font_name);
163 g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
164 g_memmove(p, item->font_name, val+1); p+= val+1;
165 g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
166 if (nitems==1) target_text = g_strdup(item->text); // single text item
168 if (item->type == ITEM_IMAGE) {
169 g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
170 g_memmove(p, &item->image_png_len, sizeof(gsize)); p+= sizeof(gsize);
171 if (item->image_png_len > 0) {
172 g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
174 if (nitems==1) target_pixbuf = gdk_pixbuf_copy(item->image); // single image
178 /* add pointers to alternative representations;
179 note those are not serialized, but they're only used internally,
180 and not sent along to the GtkSelection */
181 g_memmove(p, &target_text, sizeof(gchar *)); p+= sizeof(gchar *);
182 g_memmove(p, &target_pixbuf, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
184 /* build list of valid targets */
185 targetlist = gtk_target_list_new(NULL, 0);
186 gtk_target_list_add(targetlist,
187 gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 0, TARGET_XOURNAL);
188 if (target_pixbuf!=NULL)
189 gtk_target_list_add_image_targets(targetlist, TARGET_PIXBUF, TRUE);
190 if (target_text!=NULL)
191 gtk_target_list_add_text_targets(targetlist, TARGET_TEXT);
192 targets = gtk_target_table_new_from_list(targetlist, &n_targets);
193 gtk_target_list_unref(targetlist);
195 gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
197 callback_clipboard_get, callback_clipboard_clear, buf);
198 gtk_target_table_free(targets, n_targets);
201 // local paste within xournal
202 void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
205 int nitems, npts, i, len;
207 double hoffset, voffset, cx, cy;
213 ui.selection = g_new(struct Selection, 1);
214 p = sel_data->data + sizeof(int);
215 g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
216 ui.selection->type = ITEM_SELECTRECT;
217 ui.selection->layer = ui.cur_layer;
218 g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
219 ui.selection->items = NULL;
221 // find by how much we translate the pasted selection
222 gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
223 gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
224 gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
225 cx -= ui.cur_page->hoffset;
226 cy -= ui.cur_page->voffset;
227 if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
228 cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
229 if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
230 cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
231 if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
232 cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
233 if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
234 cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
235 hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
236 voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
237 ui.selection->bbox.left += hoffset;
238 ui.selection->bbox.right += hoffset;
239 ui.selection->bbox.top += voffset;
240 ui.selection->bbox.bottom += voffset;
242 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
243 gnome_canvas_rect_get_type(), "width-pixels", 1,
244 "outline-color-rgba", 0x000000ff,
245 "fill-color-rgba", 0x80808040,
246 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
247 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
248 make_dashed(ui.selection->canvas_item);
250 while (nitems-- > 0) {
251 item = g_new(struct Item, 1);
252 ui.selection->items = g_list_append(ui.selection->items, item);
253 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
254 ui.cur_layer->nitems++;
255 g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
256 if (item->type == ITEM_STROKE) {
257 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
258 g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
259 item->path = gnome_canvas_points_new(npts);
261 for (i=0; i<npts; i++) {
262 item->path->coords[2*i] = pf[2*i] + hoffset;
263 item->path->coords[2*i+1] = pf[2*i+1] + voffset;
265 p+= 2*item->path->num_points*sizeof(double);
266 if (item->brush.variable_width) {
267 item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
268 p+= (item->path->num_points-1)*sizeof(double);
270 else item->widths = NULL;
271 update_item_bbox(item);
272 make_canvas_item_one(ui.cur_layer->group, item);
274 if (item->type == ITEM_TEXT) {
275 g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
276 g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
277 g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
278 item->bbox.left += hoffset;
279 item->bbox.top += voffset;
280 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
281 item->text = g_malloc(len+1);
282 g_memmove(item->text, p, len+1); p+= len+1;
283 g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
284 item->font_name = g_malloc(len+1);
285 g_memmove(item->font_name, p, len+1); p+= len+1;
286 g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
287 make_canvas_item_one(ui.cur_layer->group, item);
289 if (item->type == ITEM_IMAGE) {
290 item->canvas_item = NULL;
291 item->image_png = NULL;
292 item->image_png_len = 0;
293 g_memmove(&item->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
294 item->bbox.left += hoffset;
295 item->bbox.right += hoffset;
296 item->bbox.top += voffset;
297 item->bbox.bottom += voffset;
298 g_memmove(&item->image_png_len, p, sizeof(gsize)); p+= sizeof(gsize);
299 if (item->image_png_len > 0) {
300 item->image_png = g_memdup(p, item->image_png_len);
301 item->image = pixbuf_from_buffer(item->image_png, item->image_png_len);
302 p+= item->image_png_len;
306 make_canvas_item_one(ui.cur_layer->group, item);
311 undo->type = ITEM_PASTE;
312 undo->layer = ui.cur_layer;
313 undo->itemlist = g_list_copy(ui.selection->items);
315 gtk_selection_data_free(sel_data);
316 update_copy_paste_enabled();
318 update_thickness_buttons();
319 update_color_buttons();
320 update_font_button();
321 update_cursor(); // FIXME: can't know if pointer is within selection!
324 // paste external text
325 void clipboard_paste_text(gchar *text)
331 get_current_pointer_coords(pt);
332 set_current_page(pt);
334 ui.selection = g_new(struct Selection, 1);
335 ui.selection->type = ITEM_SELECTRECT;
336 ui.selection->layer = ui.cur_layer;
337 ui.selection->items = NULL;
339 item = g_new(struct Item, 1);
340 ui.selection->items = g_list_append(ui.selection->items, item);
341 ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
342 ui.cur_layer->nitems++;
343 item->type = ITEM_TEXT;
344 g_memmove(&(item->brush), ui.cur_brush, sizeof(struct Brush));
345 item->text = text; // text was newly allocated, we keep it
346 item->font_name = g_strdup(ui.font_name);
347 item->font_size = ui.font_size;
348 item->bbox.left = pt[0]; item->bbox.top = pt[1];
349 make_canvas_item_one(ui.cur_layer->group, item);
350 update_item_bbox(item);
352 // move the text to fit on the page if needed
353 if (item->bbox.right > ui.cur_page->width) item->bbox.left += ui.cur_page->width-item->bbox.right;
354 if (item->bbox.left < 0) item->bbox.left = 0;
355 if (item->bbox.bottom > ui.cur_page->height) item->bbox.top += ui.cur_page->height-item->bbox.bottom;
356 if (item->bbox.top < 0) item->bbox.top = 0;
357 gnome_canvas_item_set(item->canvas_item, "x", item->bbox.left, "y", item->bbox.top, NULL);
358 update_item_bbox(item);
360 ui.selection->bbox = item->bbox;
361 ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
362 gnome_canvas_rect_get_type(), "width-pixels", 1,
363 "outline-color-rgba", 0x000000ff,
364 "fill-color-rgba", 0x80808040,
365 "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
366 "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
367 make_dashed(ui.selection->canvas_item);
370 undo->type = ITEM_PASTE;
371 undo->layer = ui.cur_layer;
372 undo->itemlist = g_list_copy(ui.selection->items);
374 update_copy_paste_enabled();
376 update_thickness_buttons();
377 update_color_buttons();
378 update_font_button();
379 update_cursor(); // FIXME: can't know if pointer is within selection!
382 // paste an external image
383 void clipboard_paste_image(GdkPixbuf *pixbuf)
389 get_current_pointer_coords(pt);
390 set_current_page(pt);
392 create_image_from_pixbuf(pixbuf, pt);
395 // work out what format the clipboard data is in, and paste accordingly
396 void clipboard_paste(void)
398 GtkSelectionData *sel_data;
399 GtkClipboard *clipboard;
403 if (ui.cur_layer == NULL) return;
405 ui.cur_item_type = ITEM_PASTE;
406 clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
408 sel_data = gtk_clipboard_wait_for_contents(
410 gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE));
411 ui.cur_item_type = ITEM_NONE;
412 if (sel_data != NULL) {
413 clipboard_paste_from_xournal(sel_data);
417 pixbuf = gtk_clipboard_wait_for_image(clipboard);
418 if (pixbuf != NULL) {
419 clipboard_paste_image(pixbuf);
423 text = gtk_clipboard_wait_for_text(clipboard);
425 clipboard_paste_text(text);