]> git.donarmstrong.com Git - xournal.git/commitdiff
paste images and text directly from and to other applications
authorauroux <auroux>
Sun, 1 Jul 2012 22:11:08 +0000 (22:11 +0000)
committerauroux <auroux>
Sun, 1 Jul 2012 22:11:08 +0000 (22:11 +0000)
ChangeLog
src/TODO
src/xo-clipboard.c
src/xo-interface.c
src/xo-paint.c
src/xo-paint.h

index c4f2fd045154ff862f5cbbb4524e31856e676164..43c50f0e56ef8d1f83dc5070ede9adec50413b96 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,6 +1,7 @@
 This version:
   - insert image tool (based on patches by Victor Saase and Simon Guest)
   - renamed "Journal" menu to "Page"
+  - paste images and text directly from and to other applications
 
 Version 0.4.6 (May 22, 2012):
   - win32 portability code (contributed by Dirk Gerrits)
index 01bdcd932fd14a11a55fa50930e78de0ed45b7ae..9536063b7fe0b1d3be1335f7001b428e901ea473 100644 (file)
--- a/src/TODO
+++ b/src/TODO
@@ -22,9 +22,7 @@ List of features to be implemented (not in any particular order)
 
 TODO WITH IMAGE PATCH:
 TODO: paste and selresize don't put rectangle at top
-TODO: select click in image selects it
 TODO: option for image mode to revert to previous tool
-TODO: direct paste text
 
 BUGS:
 - lingering issues with synaptics touchpads? (#2872667)
index 3aa365370f0e18db9649f762cb2fd9230764252e..3eaec5f73551f09ca96cb5b340c4bf9eb80ace87 100644 (file)
 #include "xo-paint.h"
 #include "xo-image.h"
 
+#define TARGET_XOURNAL 1
+#define TARGET_TEXT    2
+#define TARGET_PIXBUF  3
+#define XOURNAL_TARGET_ATOM "_XOURNAL" 
+  /* change when serialized data format changes incompatibly */
 
 void callback_clipboard_get(GtkClipboard *clipboard,
                             GtkSelectionData *selection_data,
                             guint info, gpointer user_data)
 {
   int length;
+  char *p;
+  gchar *text;
+  GdkPixbuf *pixbuf;
   
   g_memmove(&length, user_data, sizeof(int));
-  gtk_selection_data_set(selection_data,
-     gdk_atom_intern("_XOURNAL", FALSE), 8, user_data, length);
+  p = ((char *)user_data) + length;
+  g_memmove(&text, p, sizeof(gchar *)); p+= sizeof(gchar *);
+  g_memmove(&pixbuf, p, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
+  
+  switch (info) {
+    case TARGET_XOURNAL:
+      gtk_selection_data_set(selection_data,
+        gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE), 8, user_data, length);
+      break;
+    case TARGET_TEXT:
+      if (text!=NULL) 
+        gtk_selection_data_set_text(selection_data, text, -1);
+      break;
+    case TARGET_PIXBUF:
+      if (pixbuf!=NULL)
+        gtk_selection_data_set_pixbuf(selection_data, pixbuf);
+      break;
+  }
 }
 
 void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
 {
+  int length;
+  char *p;
+  gchar *text;
+  GdkPixbuf *pixbuf;
+  
+  g_memmove(&length, user_data, sizeof(int));
+  p = ((char *)user_data) + length;
+  g_memmove(&text, p, sizeof(gchar *)); p+= sizeof(gchar *);
+  g_memmove(&pixbuf, p, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
+
+  if (text!=NULL) g_free(text);
+  if (pixbuf!=NULL) gdk_pixbuf_unref(pixbuf);
   g_free(user_data);
 }
 
@@ -51,7 +87,11 @@ void selection_to_clip(void)
   char *buf, *p;
   GList *list;
   struct Item *item;
-  GtkTargetEntry target;
+  GtkTargetList *targetlist;
+  GtkTargetEntry *targets;
+  GdkPixbuf *target_pixbuf;
+  gchar *target_text;
+  int n_targets;
   
   if (ui.selection == NULL) return;
   bufsz = 2*sizeof(int) // bufsz, nitems
@@ -92,7 +132,10 @@ void selection_to_clip(void)
     }
     else bufsz+= sizeof(int); // type
   }
-  p = buf = g_malloc(bufsz);
+  // allocate buffer; allow space for alternative target representations at the end
+  p = buf = g_malloc(bufsz + sizeof(gchar *) + sizeof(GdkPixbuf *));
+  target_pixbuf = NULL;
+  target_text = NULL;
   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);
@@ -120,6 +163,7 @@ void selection_to_clip(void)
       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) target_text = g_strdup(item->text); // single text item
     }
     if (item->type == ITEM_IMAGE) {
       g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
@@ -127,16 +171,31 @@ void selection_to_clip(void)
       if (item->image_png_len > 0) {
         g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
       }
+      if (nitems==1) target_pixbuf = gdk_pixbuf_copy(item->image); // single image
     }
   }
   
-  target.target = "_XOURNAL";
-  target.flags = 0;
-  target.info = 0;
+  /* add pointers to alternative representations; 
+     note those are not serialized, but they're only used internally,
+     and not sent along to the GtkSelection */
+  g_memmove(p, &target_text, sizeof(gchar *)); p+= sizeof(gchar *);
+  g_memmove(p, &target_pixbuf, sizeof(GdkPixbuf *)); p += sizeof(GdkPixbuf *);
+  
+  /* 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 (target_pixbuf!=NULL)
+    gtk_target_list_add_image_targets(targetlist, TARGET_PIXBUF, TRUE);
+  if (target_text!=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,
+       targets, n_targets,
        callback_clipboard_get, callback_clipboard_clear, buf);
+  gtk_target_table_free(targets, n_targets);
 }
 
 // local paste within xournal
@@ -262,6 +321,64 @@ void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
   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.cur_brush, 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)
 {
@@ -281,6 +398,7 @@ void clipboard_paste(void)
   GtkSelectionData *sel_data;
   GtkClipboard *clipboard;
   GdkPixbuf *pixbuf;
+  gchar *text;
 
   if (ui.cur_layer == NULL) return;
   
@@ -289,7 +407,7 @@ void clipboard_paste(void)
   // try xournal data
   sel_data = gtk_clipboard_wait_for_contents(
       clipboard,
-      gdk_atom_intern("_XOURNAL", FALSE));
+      gdk_atom_intern(XOURNAL_TARGET_ATOM, FALSE));
   ui.cur_item_type = ITEM_NONE;
   if (sel_data != NULL) { 
     clipboard_paste_from_xournal(sel_data);
@@ -301,4 +419,10 @@ void clipboard_paste(void)
     clipboard_paste_image(pixbuf);
     return;
   }
+  // try text data
+  text = gtk_clipboard_wait_for_text(clipboard);
+  if (text != NULL) {
+    clipboard_paste_text(text);
+    return;
+  }
 }
index 00a72d1780f0ebb06a7c6e95bdd895aa60d99ff4..b4d71e8fa9fa36c0371cdec6697b4c926ef58037 100644 (file)
@@ -1614,13 +1614,13 @@ create_winMain (void)
   buttonPen_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (buttonText));
 
   buttonImage = (GtkWidget*) gtk_radio_tool_button_new (NULL);
-  gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonImage), _("Text"));
+  gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonImage), _("Image"));
   tmp_image = gtk_image_new_from_stock ("gtk-orientation-portrait", tmp_toolbar_icon_size);
   gtk_widget_show (tmp_image);
   gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (buttonImage), tmp_image);
   gtk_widget_show (buttonImage);
   gtk_container_add (GTK_CONTAINER (toolbarPen), buttonImage);
-  gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (buttonImage), tooltips, _("Text"), NULL);
+  gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (buttonImage), tooltips, _("Image"), NULL);
   gtk_radio_tool_button_set_group (GTK_RADIO_TOOL_BUTTON (buttonImage), buttonPen_group);
   buttonPen_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (buttonImage));
 
index 33576f8a21cca3fc5ccbef072e631fb5d9f4e361..15b1fe86f65aacbb9958aa71cd57b16f35da77ec 100644 (file)
@@ -608,9 +608,9 @@ void finalize_selectrect(void)
   }
   
   if (ui.selection->items == NULL) {
-    // if we clicked inside a text zone ?  
-    item = click_is_in_text(ui.selection->layer, x1, y1);
-    if (item!=NULL && item==click_is_in_text(ui.selection->layer, x2, y2)) {
+    // if we clicked inside a text zone or image?  
+    item = click_is_in_text_or_image(ui.selection->layer, x1, y1);
+    if (item!=NULL && item==click_is_in_text_or_image(ui.selection->layer, x2, y2)) {
       ui.selection->items = g_list_append(ui.selection->items, item);
       g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox));
       gnome_canvas_item_set(ui.selection->canvas_item,
@@ -1259,6 +1259,22 @@ struct Item *click_is_in_text(struct Layer *layer, double x, double y)
   return val;
 }
 
+struct Item *click_is_in_text_or_image(struct Layer *layer, double x, double y)
+{
+  GList *itemlist;
+  struct Item *item, *val;
+  
+  val = NULL;
+  for (itemlist = layer->items; itemlist!=NULL; itemlist = itemlist->next) {
+    item = (struct Item *)itemlist->data;
+    if (item->type != ITEM_TEXT && item->type != ITEM_IMAGE) continue;
+    if (x<item->bbox.left || x>item->bbox.right) continue;
+    if (y<item->bbox.top || y>item->bbox.bottom) continue;
+    val = item;
+  }
+  return val;
+}
+
 void refont_text_item(struct Item *item, gchar *font_name, double font_size)
 {
   if (!strcmp(font_name, item->font_name) && font_size==item->font_size) return;
index 8d69bd68efed31358f82d5a03894aefcec003b77..9a30df86c2001ec4b334e4c4780b834b142c0a50 100644 (file)
@@ -52,5 +52,6 @@ void end_text(void);
 void update_text_item_displayfont(struct Item *item);
 void rescale_text_items(void);
 struct Item *click_is_in_text(struct Layer *layer, double x, double y);
+struct Item *click_is_in_text_or_image(struct Layer *layer, double x, double y);
 void refont_text_item(struct Item *item, gchar *font_name, double font_size);
 void process_font_sel(gchar *str);