#include "xo-paint.h"
#include "xo-image.h"
+// the various formats in which we might present clipboard data
+#define TARGET_XOURNAL 1
+#define TARGET_TEXT 2
+#define TARGET_PIXBUF 3
+#define XOURNAL_TARGET_ATOM "_XOURNAL"
+ /* change when serialized data format changes incompatibly */
+
+typedef struct XojSelectionData {
+ int xo_data_len;
+ char *xo_data;
+ gchar *text_data;
+ GdkPixbuf *image_data;
+} XojSelectionData;
+
void callback_clipboard_get(GtkClipboard *clipboard,
GtkSelectionData *selection_data,
guint info, gpointer user_data)
{
- int length;
-
- g_memmove(&length, user_data, sizeof(int));
- gtk_selection_data_set(selection_data,
- gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
+ struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
+
+ switch (info) {
+ case TARGET_XOURNAL:
+ gtk_selection_data_set(selection_data,
+ gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 8, sel->xo_data, sel->xo_data_len);
+ break;
+ case TARGET_TEXT:
+ if (sel->text_data!=NULL)
+ gtk_selection_data_set_text(selection_data, sel->text_data, -1);
+ break;
+ case TARGET_PIXBUF:
+ if (sel->image_data!=NULL)
+ gtk_selection_data_set_pixbuf(selection_data, sel->image_data);
+ break;
+ }
}
void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
{
- g_free(user_data);
+ struct XojSelectionData *sel = (struct XojSelectionData *)user_data;
+
+ if (sel->xo_data!=NULL) g_free(sel->xo_data);
+ if (sel->text_data!=NULL) g_free(sel->text_data);
+ if (sel->image_data!=NULL) gdk_pixbuf_unref(sel->image_data);
+ g_free(sel);
}
void selection_to_clip(void)
{
+ struct XojSelectionData *sel;
int bufsz, nitems, val;
- char *buf, *p;
+ char *p;
GList *list;
struct Item *item;
- GtkTargetEntry target;
+ GtkTargetList *targetlist;
+ GtkTargetEntry *targets;
+ int n_targets;
if (ui.selection == NULL) return;
bufsz = 2*sizeof(int) // bufsz, nitems
}
else bufsz+= sizeof(int); // type
}
- p = buf = g_malloc(bufsz);
+
+ // allocate selection data structure and buffer
+ sel = g_malloc(sizeof(struct XojSelectionData));
+ sel->xo_data_len = bufsz;
+ sel->xo_data = g_malloc(bufsz);
+ sel->image_data = NULL;
+ sel->text_data = NULL;
+
+ // fill in the data
+ p = sel->xo_data;
g_memmove(p, &bufsz, sizeof(int)); p+= sizeof(int);
g_memmove(p, &nitems, sizeof(int)); p+= sizeof(int);
g_memmove(p, &ui.selection->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
g_memmove(p, item->font_name, val+1); p+= val+1;
g_memmove(p, &item->font_size, sizeof(double)); p+= sizeof(double);
+ if (nitems==1) sel->text_data = g_strdup(item->text); // single text item
}
if (item->type == ITEM_IMAGE) {
g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
if (item->image_png_len > 0) {
g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
}
+ if (nitems==1) sel->image_data = gdk_pixbuf_copy(item->image); // single image
}
}
- target.target = "_XOURNAL";
- target.flags = 0;
- target.info = 0;
+ /* build list of valid targets */
+ targetlist = gtk_target_list_new(NULL, 0);
+ gtk_target_list_add(targetlist,
+ gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 0, TARGET_XOURNAL);
+ if (sel->image_data!=NULL)
+ gtk_target_list_add_image_targets(targetlist, TARGET_PIXBUF, TRUE);
+ if (sel->text_data!=NULL)
+ gtk_target_list_add_text_targets(targetlist, TARGET_TEXT);
+ targets = gtk_target_table_new_from_list(targetlist, &n_targets);
+ gtk_target_list_unref(targetlist);
gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
- &target, 1,
- callback_clipboard_get, callback_clipboard_clear, buf);
+ targets, n_targets,
+ callback_clipboard_get, callback_clipboard_clear, sel);
+ gtk_target_table_free(targets, n_targets);
}
-// local paste within xournal
+// paste xournal native data
void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
{
unsigned char *p;
update_cursor(); // FIXME: can't know if pointer is within selection!
}
+// paste external text
+void clipboard_paste_text(gchar *text)
+{
+ struct Item *item;
+ double pt[2];
+
+ reset_selection();
+ get_current_pointer_coords(pt);
+ set_current_page(pt);
+
+ ui.selection = g_new(struct Selection, 1);
+ ui.selection->type = ITEM_SELECTRECT;
+ ui.selection->layer = ui.cur_layer;
+ ui.selection->items = NULL;
+
+ item = g_new(struct Item, 1);
+ ui.selection->items = g_list_append(ui.selection->items, item);
+ ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
+ ui.cur_layer->nitems++;
+ item->type = ITEM_TEXT;
+ g_memmove(&(item->brush), &(ui.brushes[ui.cur_mapping][TOOL_PEN]), sizeof(struct Brush));
+ item->text = text; // text was newly allocated, we keep it
+ item->font_name = g_strdup(ui.font_name);
+ item->font_size = ui.font_size;
+ item->bbox.left = pt[0]; item->bbox.top = pt[1];
+ make_canvas_item_one(ui.cur_layer->group, item);
+ update_item_bbox(item);
+
+ // move the text to fit on the page if needed
+ if (item->bbox.right > ui.cur_page->width) item->bbox.left += ui.cur_page->width-item->bbox.right;
+ if (item->bbox.left < 0) item->bbox.left = 0;
+ if (item->bbox.bottom > ui.cur_page->height) item->bbox.top += ui.cur_page->height-item->bbox.bottom;
+ if (item->bbox.top < 0) item->bbox.top = 0;
+ gnome_canvas_item_set(item->canvas_item, "x", item->bbox.left, "y", item->bbox.top, NULL);
+ update_item_bbox(item);
+
+ ui.selection->bbox = item->bbox;
+ ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group,
+ gnome_canvas_rect_get_type(), "width-pixels", 1,
+ "outline-color-rgba", 0x000000ff,
+ "fill-color-rgba", 0x80808040,
+ "x1", ui.selection->bbox.left, "x2", ui.selection->bbox.right,
+ "y1", ui.selection->bbox.top, "y2", ui.selection->bbox.bottom, NULL);
+ make_dashed(ui.selection->canvas_item);
+
+ prepare_new_undo();
+ undo->type = ITEM_PASTE;
+ undo->layer = ui.cur_layer;
+ undo->itemlist = g_list_copy(ui.selection->items);
+
+ update_copy_paste_enabled();
+ update_color_menu();
+ update_thickness_buttons();
+ update_color_buttons();
+ update_font_button();
+ update_cursor(); // FIXME: can't know if pointer is within selection!
+}
+
// paste an external image
void clipboard_paste_image(GdkPixbuf *pixbuf)
{
GtkSelectionData *sel_data;
GtkClipboard *clipboard;
GdkPixbuf *pixbuf;
+ gchar *text;
if (ui.cur_layer == NULL) return;
// try xournal data
sel_data = gtk_clipboard_wait_for_contents(
clipboard,
- gdk_atom_intern("_XOURNAL", FALSE));
+ gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE));
+#ifdef WIN32 // avoid a win32 bug showing images as xournal data
+ if (gtk_selection_data_get_data_type(sel_data)!=gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE))
+ { gtk_selection_data_free(sel_data); sel_data = NULL; }
+#endif
ui.cur_item_type = ITEM_NONE;
if (sel_data != NULL) {
clipboard_paste_from_xournal(sel_data);
clipboard_paste_image(pixbuf);
return;
}
+ // try text data
+ text = gtk_clipboard_wait_for_text(clipboard);
+ if (text != NULL) {
+ clipboard_paste_text(text);
+ return;
+ }
}