]> git.donarmstrong.com Git - xournal.git/commitdiff
Image patch
authorauroux <auroux>
Thu, 28 Jun 2012 22:20:32 +0000 (22:20 +0000)
committerauroux <auroux>
Thu, 28 Jun 2012 22:20:32 +0000 (22:20 +0000)
23 files changed:
AUTHORS
ChangeLog
configure
configure.in
src/Makefile.am
src/Makefile.in
src/TODO
src/xo-callbacks.c
src/xo-callbacks.h
src/xo-clipboard.c [new file with mode: 0644]
src/xo-clipboard.h [new file with mode: 0644]
src/xo-file.c
src/xo-image.c [new file with mode: 0644]
src/xo-image.h [new file with mode: 0644]
src/xo-interface.c
src/xo-misc.c
src/xo-misc.h
src/xo-paint.c
src/xo-paint.h
src/xo-print.c
src/xo-print.h
src/xournal.h
xournal.glade

diff --git a/AUTHORS b/AUTHORS
index fadb86c03fe94909024af9eb199f03de8883cd11..7950cc0a06fdef3e495db9c55fdfdfcc18e4b654 100644 (file)
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,10 +4,11 @@ The source code includes contributions by the following people:
 
 Alvaro, Kit Barnes, Eduardo de Barros Lima, Mathieu Bouchard, 
 Ole Joergen Broenner, Robert Buchholz, Vincenzo Ciancia, Luca de Cicco, 
 
 Alvaro, Kit Barnes, Eduardo de Barros Lima, Mathieu Bouchard, 
 Ole Joergen Broenner, Robert Buchholz, Vincenzo Ciancia, Luca de Cicco, 
-Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits,
+Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits, Simon Guest,
 Lukasz Kaiser, Timo Kluck, David Kolibac, Danny Kukawka, Stefan Lembach, 
 Bob McElrath, Andy Neitzke, David Planella, Marco Poletti, Alex Ray, 
 Lukasz Kaiser, Timo Kluck, David Kolibac, Danny Kukawka, Stefan Lembach, 
 Bob McElrath, Andy Neitzke, David Planella, Marco Poletti, Alex Ray, 
-Jean-Baptiste Rouquier, Marco Souza, Mike Ter Louw, Uwe Winter, Lu Zhihe.
+Jean-Baptiste Rouquier, Victor Saase, Marco Souza, Mike Ter Louw, 
+Uwe Winter, Lu Zhihe.
 
 (Let me know if you are missing from this list or if your name is 
 mis-spelled)
 
 (Let me know if you are missing from this list or if your name is 
 mis-spelled)
index dd5f70a047b239043342c3048e82174bee5eaeb9..c4f2fd045154ff862f5cbbb4524e31856e676164 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,7 @@
+This version:
+  - insert image tool (based on patches by Victor Saase and Simon Guest)
+  - renamed "Journal" menu to "Page"
+
 Version 0.4.6 (May 22, 2012):
   - win32 portability code (contributed by Dirk Gerrits)
   - fix bug in PDF export code on 64-bit systems (patch by Robert Buchholz)
 Version 0.4.6 (May 22, 2012):
   - win32 portability code (contributed by Dirk Gerrits)
   - fix bug in PDF export code on 64-bit systems (patch by Robert Buchholz)
index f3c87c82ec8467853434d09235f96c6df4cb7219..1f0cd5d7d8e8fe4421a9571b575400b1909d88c7 100755 (executable)
--- a/configure
+++ b/configure
@@ -2627,7 +2627,7 @@ fi
 
 # Define the identity of the package.
  PACKAGE=xournal
 
 # Define the identity of the package.
  PACKAGE=xournal
- VERSION=0.4.6
+ VERSION=0.4.6+image
 
 
 cat >>confdefs.h <<_ACEOF
 
 
 cat >>confdefs.h <<_ACEOF
index 5d4aa09d3c0b5e9863ecda0aa727eecbfcd0407a..99d500bb64b838b1453fb8ec8e6686a1e82260af 100644 (file)
@@ -1,7 +1,7 @@
 dnl Process this file with autoconf to produce a configure script.
 
 AC_INIT(configure.in)
 dnl Process this file with autoconf to produce a configure script.
 
 AC_INIT(configure.in)
-AM_INIT_AUTOMAKE(xournal, 0.4.6)
+AM_INIT_AUTOMAKE(xournal, 0.4.6+image)
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
 AM_CONFIG_HEADER(config.h)
 AM_MAINTAINER_MODE
 
index 03ecd359df28532db8639bab416b07a7919bad95..55aa52c6a4533b66073f3bf44e239f58feaaa2bb 100644 (file)
@@ -18,6 +18,8 @@ xournal_SOURCES = \
        xo-misc.c xo-misc.h \
        xo-file.c xo-file.h \
        xo-paint.c xo-paint.h \
        xo-misc.c xo-misc.h \
        xo-file.c xo-file.h \
        xo-paint.c xo-paint.h \
+       xo-clipboard.c xo-clipboard.h \
+       xo-image.c xo-image.h \
        xo-print.c xo-print.h \
        xo-support.c xo-support.h \
        xo-interface.c xo-interface.h \
        xo-print.c xo-print.h \
        xo-support.c xo-support.h \
        xo-interface.c xo-interface.h \
@@ -26,8 +28,8 @@ xournal_SOURCES = \
 
 if WIN32
   xournal_LDFLAGS = -mwindows
 
 if WIN32
   xournal_LDFLAGS = -mwindows
-  xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS)
+  xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lz
 else
 else
-  xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11
+  xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11 -lz
 endif
 
 endif
 
index 45ebffa3456dde36eba7bfdb0c2d60d4725dfe27..a0bbbe9757ba7e4a8dc0199a0ca32451a27c2d8f 100644 (file)
@@ -48,9 +48,10 @@ CONFIG_CLEAN_VPATH_FILES =
 am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
 am_xournal_OBJECTS = main.$(OBJEXT) xo-misc.$(OBJEXT) \
 am__installdirs = "$(DESTDIR)$(bindir)"
 PROGRAMS = $(bin_PROGRAMS)
 am_xournal_OBJECTS = main.$(OBJEXT) xo-misc.$(OBJEXT) \
-       xo-file.$(OBJEXT) xo-paint.$(OBJEXT) xo-print.$(OBJEXT) \
-       xo-support.$(OBJEXT) xo-interface.$(OBJEXT) \
-       xo-callbacks.$(OBJEXT) xo-shapes.$(OBJEXT)
+       xo-file.$(OBJEXT) xo-paint.$(OBJEXT) xo-clipboard.$(OBJEXT) \
+       xo-image.$(OBJEXT) xo-print.$(OBJEXT) xo-support.$(OBJEXT) \
+       xo-interface.$(OBJEXT) xo-callbacks.$(OBJEXT) \
+       xo-shapes.$(OBJEXT)
 xournal_OBJECTS = $(am_xournal_OBJECTS)
 am__DEPENDENCIES_1 =
 @WIN32_FALSE@xournal_DEPENDENCIES = ttsubset/libttsubset.a \
 xournal_OBJECTS = $(am_xournal_OBJECTS)
 am__DEPENDENCIES_1 =
 @WIN32_FALSE@xournal_DEPENDENCIES = ttsubset/libttsubset.a \
@@ -240,6 +241,8 @@ xournal_SOURCES = \
        xo-misc.c xo-misc.h \
        xo-file.c xo-file.h \
        xo-paint.c xo-paint.h \
        xo-misc.c xo-misc.h \
        xo-file.c xo-file.h \
        xo-paint.c xo-paint.h \
+       xo-clipboard.c xo-clipboard.h \
+       xo-image.c xo-image.h \
        xo-print.c xo-print.h \
        xo-support.c xo-support.h \
        xo-interface.c xo-interface.h \
        xo-print.c xo-print.h \
        xo-support.c xo-support.h \
        xo-interface.c xo-interface.h \
@@ -247,8 +250,8 @@ xournal_SOURCES = \
        xo-shapes.c xo-shapes.h
 
 @WIN32_TRUE@xournal_LDFLAGS = -mwindows
        xo-shapes.c xo-shapes.h
 
 @WIN32_TRUE@xournal_LDFLAGS = -mwindows
-@WIN32_FALSE@xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11
-@WIN32_TRUE@xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS)
+@WIN32_FALSE@xournal_LDADD = ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lX11 -lz
+@WIN32_TRUE@xournal_LDADD = win32/xournal.res ttsubset/libttsubset.a @PACKAGE_LIBS@ $(INTLLIBS) -lz
 all: all-recursive
 
 .SUFFIXES:
 all: all-recursive
 
 .SUFFIXES:
@@ -332,7 +335,9 @@ distclean-compile:
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-callbacks.Po@am__quote@
 
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/main.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-callbacks.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-clipboard.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-file.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-file.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-image.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-interface.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-misc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-paint.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-interface.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-misc.Po@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/xo-paint.Po@am__quote@
index e6d19ed52d5fcffca02818d1e56acf954918e0e6..01bdcd932fd14a11a55fa50930e78de0ed45b7ae 100644 (file)
--- a/src/TODO
+++ b/src/TODO
@@ -20,6 +20,12 @@ List of features to be implemented (not in any particular order)
   xournal). (cf #2944459). Recalibration probably requires talking directly 
   to X server to query input device geometry and compensate for GTK.
 
   xournal). (cf #2944459). Recalibration probably requires talking directly 
   to X server to query input device geometry and compensate for GTK.
 
+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)
  (todo) - set device to Absolute mode at startup? (GDK doesn't expose API,
 BUGS:
 - lingering issues with synaptics touchpads? (#2872667)
  (todo) - set device to Absolute mode at startup? (GDK doesn't expose API,
@@ -29,6 +35,11 @@ BUGS:
 - export to PDF should see cropbox size if smaller, and map the
    cropbox -- not the entire page -- to the page area! (2009-11-18 forum)
 
 - export to PDF should see cropbox size if smaller, and map the
    cropbox -- not the entire page -- to the page area! (2009-11-18 forum)
 
+BEHAVIOR TODO:
+- option to have "Save Prefs" save current brushes as settings, not defaults
+- method to map more general devices to specific tools or to Ignore
+  (e.g.: X220T ignore trackpoint; map touchscreen to Hand, eraser to Eraser)
+
 WIN32:
 - test further
 - write an installer
 WIN32:
 - test further
 - write an installer
@@ -150,10 +161,9 @@ https://sourceforge.net/tracker/index.php?func=detail&aid=3447356&group_id=16343
 - proximity detection: eraser proximity switches mapping? 
   proximity out removes cursor until next motionnotify?
 
 - proximity detection: eraser proximity switches mapping? 
   proximity out removes cursor until next motionnotify?
 
-
 - render page to bitmap: for export, preview, and copy-paste
     (render using libart, see how gnomecanvas does it?)
 - render page to bitmap: for export, preview, and copy-paste
     (render using libart, see how gnomecanvas does it?)
-    NO: render using Cairo !!! then can switch to GtkPrint as well.
+    NO: render using Cairo !!!
     (copy-paste: config option to render only current layer or all below?)
 - cut-and-paste of selection into other apps (as bitmap; as SVG?)
 
     (copy-paste: config option to render only current layer or all below?)
 - cut-and-paste of selection into other apps (as bitmap; as SVG?)
 
@@ -184,10 +194,6 @@ https://sourceforge.net/tracker/index.php?func=detail&aid=3447356&group_id=16343
 - add config option to limit total memory usage for PDF bitmaps
 - ability to select entire page for copy-paste (as bitmap / reorder xournal)
 - copy/paste of an entire page (beware if PDF bg is not compatible!)
 - add config option to limit total memory usage for PDF bitmaps
 - ability to select entire page for copy-paste (as bitmap / reorder xournal)
 - copy/paste of an entire page (beware if PDF bg is not compatible!)
-- rewrite printing using GtkPrint + Cairo as GnomePrint replacement
-   (keep GnomePrint option for compatibility with GTK+ <2.10)
-- insert images (screen capture or from file or from clipboard), 
-  not as full-page backgrounds (new ITEM type)
 
 - convert to/from Jarnal format; to/from MS Journal format???
 - export as SVG, as bitmap (use Cairo for this)
 
 - convert to/from Jarnal format; to/from MS Journal format???
 - export as SVG, as bitmap (use Cairo for this)
index 3b2b3199421c8e5c5c034d0b1721ef69081735cc..8c6f098a1cdc88566305da6b6181386ad3960a0d 100644 (file)
@@ -468,7 +468,7 @@ on_editUndo_activate                   (GtkMenuItem     *menuitem,
   if (undo == NULL) return; // nothing to undo!
   reset_selection(); // safer
   reset_recognizer(); // safer
   if (undo == NULL) return; // nothing to undo!
   reset_selection(); // safer
   reset_recognizer(); // safer
-  if (undo->type == ITEM_STROKE || undo->type == ITEM_TEXT) {
+  if (undo->type == ITEM_STROKE || undo->type == ITEM_TEXT || undo->type == ITEM_IMAGE) {
     // we're keeping the stroke info, but deleting the canvas item
     gtk_object_destroy(GTK_OBJECT(undo->item->canvas_item));
     undo->item->canvas_item = NULL;
     // we're keeping the stroke info, but deleting the canvas item
     gtk_object_destroy(GTK_OBJECT(undo->item->canvas_item));
     undo->item->canvas_item = NULL;
@@ -682,7 +682,7 @@ on_editRedo_activate                   (GtkMenuItem     *menuitem,
   if (redo == NULL) return; // nothing to redo!
   reset_selection(); // safer
   reset_recognizer(); // safer
   if (redo == NULL) return; // nothing to redo!
   reset_selection(); // safer
   reset_recognizer(); // safer
-  if (redo->type == ITEM_STROKE || redo->type == ITEM_TEXT) {
+  if (redo->type == ITEM_STROKE || redo->type == ITEM_TEXT || redo->type == ITEM_IMAGE) {
     // re-create the canvas_item
     make_canvas_item_one(redo->layer->group, redo->item);
     // reinsert the item on its layer
     // re-create the canvas_item
     make_canvas_item_one(redo->layer->group, redo->item);
     // reinsert the item on its layer
@@ -840,6 +840,12 @@ on_editRedo_activate                   (GtkMenuItem     *menuitem,
       if (it->type == ITEM_TEXT && it->canvas_item != NULL)
         gnome_canvas_item_set(it->canvas_item, 
           "fill-color-rgba", it->brush.color_rgba, NULL);
       if (it->type == ITEM_TEXT && it->canvas_item != NULL)
         gnome_canvas_item_set(it->canvas_item, 
           "fill-color-rgba", it->brush.color_rgba, NULL);
+      if (it->type == ITEM_IMAGE && it->canvas_item != NULL) {
+        // remark: a variable-width item might have lost its variable-width
+        group = (GnomeCanvasGroup *) it->canvas_item->parent;
+        gtk_object_destroy(GTK_OBJECT(it->canvas_item));
+        make_canvas_item_one(group, it);
+      }
     }
   }
   else if (redo->type == ITEM_TEXT_EDIT) {
     }
   }
   else if (redo->type == ITEM_TEXT_EDIT) {
@@ -962,6 +968,7 @@ on_viewZoomIn_activate                 (GtkMenuItem     *menuitem,
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
+  rescale_images();
 }
 
 
 }
 
 
@@ -974,6 +981,7 @@ on_viewZoomOut_activate                (GtkMenuItem     *menuitem,
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
+  rescale_images();
 }
 
 
 }
 
 
@@ -985,6 +993,7 @@ on_viewNormalSize_activate             (GtkMenuItem     *menuitem,
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
+  rescale_images();
 }
 
 
 }
 
 
@@ -996,6 +1005,7 @@ on_viewPageWidth_activate              (GtkMenuItem     *menuitem,
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
   gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
   rescale_text_items();
   rescale_bg_pixmaps();
+  rescale_images();
 }
 
 
 }
 
 
@@ -1555,6 +1565,7 @@ on_journalLoadBackground_activate      (GtkMenuItem     *menuitem,
     gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
     rescale_text_items();
     rescale_bg_pixmaps();
     gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
     rescale_text_items();
     rescale_bg_pixmaps();
+    rescale_images();
   }
   do_switch_page(ui.pageno, TRUE, TRUE);
 }
   }
   do_switch_page(ui.pageno, TRUE, TRUE);
 }
@@ -1601,6 +1612,7 @@ on_journalScreenshot_activate          (GtkMenuItem     *menuitem,
     gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
     rescale_text_items();
     rescale_bg_pixmaps();
     gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
     rescale_text_items();
     rescale_bg_pixmaps();
+    rescale_images();
   }
   do_switch_page(ui.pageno, TRUE, TRUE);
 }
   }
   do_switch_page(ui.pageno, TRUE, TRUE);
 }
@@ -1764,6 +1776,33 @@ on_toolsText_activate                  (GtkMenuItem     *menuitem,
 }
 
 
 }
 
 
+void
+on_toolsImage_activate                 (GtkMenuItem *menuitem,
+                                        gpointer         user_data)
+{
+  if (GTK_OBJECT_TYPE(menuitem) == GTK_TYPE_RADIO_MENU_ITEM) {
+    if (!gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem)))
+      return;
+  } else {
+    if (!gtk_toggle_tool_button_get_active(GTK_TOGGLE_TOOL_BUTTON (menuitem)))
+      return;
+  }
+  
+  if (ui.cur_mapping != 0 && !ui.button_switch_mapping) return; // not user-generated
+  if (ui.toolno[ui.cur_mapping] == TOOL_IMAGE) return;
+  
+  ui.cur_mapping = 0; // don't use switch_mapping() (refreshes buttons too soon)
+  end_text();
+  reset_selection();
+  ui.toolno[ui.cur_mapping] = TOOL_IMAGE;
+  update_mapping_linkings(-1);
+  update_tool_buttons();
+  update_tool_menu();
+  update_color_menu();
+  update_cursor();
+}
+
+
 void
 on_toolsSelectRegion_activate          (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 void
 on_toolsSelectRegion_activate          (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
@@ -2337,8 +2376,6 @@ on_canvas_button_press_event           (GtkWidget       *widget,
                                         gpointer         user_data)
 {
   double pt[2];
                                         gpointer         user_data)
 {
   double pt[2];
-  gboolean page_change;
-  struct Page *tmppage;
   GtkWidget *dialog;
   int mapping;
   gboolean is_core;
   GtkWidget *dialog;
   int mapping;
   gboolean is_core;
@@ -2425,24 +2462,8 @@ on_canvas_button_press_event           (GtkWidget       *widget,
   else mapping = event->button-1;
 
   // check whether we're in a page
   else mapping = event->button-1;
 
   // check whether we're in a page
-  page_change = FALSE;
-  tmppage = ui.cur_page;
   get_pointer_coords((GdkEvent *)event, pt);
   get_pointer_coords((GdkEvent *)event, pt);
-  while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
-    if (ui.pageno == 0) break;
-    page_change = TRUE;
-    ui.pageno--;
-    tmppage = g_list_nth_data(journal.pages, ui.pageno);
-    pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
-  }
-  while (ui.view_continuous && (pt[1] > tmppage->height + VIEW_CONTINUOUS_SKIP)) {
-    if (ui.pageno == journal.npages-1) break;
-    pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
-    page_change = TRUE;
-    ui.pageno++;
-    tmppage = g_list_nth_data(journal.pages, ui.pageno);
-  }
-  if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
+  set_current_page(pt);
   
   // can't paint on the background...
 
   
   // can't paint on the background...
 
@@ -2508,6 +2529,9 @@ on_canvas_button_press_event           (GtkWidget       *widget,
   else if (ui.toolno[mapping] == TOOL_TEXT) {
     start_text((GdkEvent *)event, NULL);
   }
   else if (ui.toolno[mapping] == TOOL_TEXT) {
     start_text((GdkEvent *)event, NULL);
   }
+  else if (ui.toolno[mapping] == TOOL_IMAGE) {
+    insert_image((GdkEvent *)event);
+  }
   return FALSE;
 }
 
   return FALSE;
 }
 
@@ -2697,7 +2721,7 @@ on_canvas_motion_notify_event          (GtkWidget       *widget,
      or if there's a selection (then we might want to change the mouse
      cursor to indicate the possibility of resizing) */  
   if (ui.cur_item_type == ITEM_NONE && ui.selection==NULL) return FALSE;
      or if there's a selection (then we might want to change the mouse
      cursor to indicate the possibility of resizing) */  
   if (ui.cur_item_type == ITEM_NONE && ui.selection==NULL) return FALSE;
-  if (ui.cur_item_type == ITEM_TEXT) return FALSE;
+  if (ui.cur_item_type == ITEM_TEXT || ui.cur_item_type == ITEM_IMAGE) return FALSE;
 
   is_core = (event->device == gdk_device_get_core_pointer());
   if (!ui.use_xinput && !is_core) return FALSE;
 
   is_core = (event->device == gdk_device_get_core_pointer());
   if (!ui.use_xinput && !is_core) return FALSE;
@@ -3203,6 +3227,14 @@ on_button2Text_activate                (GtkMenuItem     *menuitem,
 }
 
 
 }
 
 
+void
+on_button2Image_activate               (GtkMenuItem     *menuitem,
+                                        gpointer         user_data)
+{
+  process_mapping_activate(menuitem, 1, TOOL_IMAGE);
+}
+
+
 void
 on_button2SelectRegion_activate        (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 void
 on_button2SelectRegion_activate        (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
@@ -3288,6 +3320,14 @@ on_button3Text_activate                (GtkMenuItem     *menuitem,
 }
 
 
 }
 
 
+void
+on_button3Image_activate               (GtkMenuItem     *menuitem,
+                                        gpointer         user_data)
+{
+  process_mapping_activate(menuitem, 2, TOOL_IMAGE);
+}
+
+
 void
 on_button3SelectRegion_activate        (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
 void
 on_button3SelectRegion_activate        (GtkMenuItem     *menuitem,
                                         gpointer         user_data)
@@ -3381,6 +3421,7 @@ on_viewSetZoom_activate                (GtkMenuItem     *menuitem,
       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
       rescale_text_items();
       rescale_bg_pixmaps();
       gnome_canvas_set_pixels_per_unit(canvas, ui.zoom);
       rescale_text_items();
       rescale_bg_pixmaps();
+      rescale_images();
     }
   } while (response == GTK_RESPONSE_APPLY);
   
     }
   } while (response == GTK_RESPONSE_APPLY);
   
@@ -3604,3 +3645,4 @@ on_optionsButtonsSwitchMappings_activate(GtkMenuItem    *menuitem,
   ui.button_switch_mapping = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem));
 }
 
   ui.button_switch_mapping = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM (menuitem));
 }
 
+
index d3d173ed5c5ea0fa69a6c3d1b6facd3af405f01e..bce8b486e40daf0c8b537f66713d44d95303ffe0 100644 (file)
@@ -648,3 +648,15 @@ on_buttonColorChooser_set              (GtkColorButton  *colorbutton,
 void
 on_optionsButtonsSwitchMappings_activate(GtkMenuItem    *menuitem,
                                         gpointer         user_data);
 void
 on_optionsButtonsSwitchMappings_activate(GtkMenuItem    *menuitem,
                                         gpointer         user_data);
+
+void
+on_toolsImage_activate                 (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
+
+void
+on_button2Image_activate               (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
+
+void
+on_button3Image_activate               (GtkMenuItem     *menuitem,
+                                        gpointer         user_data);
diff --git a/src/xo-clipboard.c b/src/xo-clipboard.c
new file mode 100644 (file)
index 0000000..3aa3653
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <string.h>
+#include <gtk/gtk.h>
+
+#include "xournal.h"
+#include "xo-callbacks.h"
+#include "xo-interface.h"
+#include "xo-support.h"
+#include "xo-misc.h"
+#include "xo-paint.h"
+#include "xo-image.h"
+
+
+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);
+}
+
+void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
+{
+  g_free(user_data);
+}
+
+void selection_to_clip(void)
+{
+  int bufsz, nitems, val;
+  char *buf, *p;
+  GList *list;
+  struct Item *item;
+  GtkTargetEntry target;
+  
+  if (ui.selection == NULL) return;
+  bufsz = 2*sizeof(int) // bufsz, nitems
+        + sizeof(struct BBox); // bbox
+  nitems = 0;
+  for (list = ui.selection->items; list != NULL; list = list->next) {
+    item = (struct Item *)list->data;
+    nitems++;
+    if (item->type == ITEM_STROKE) {
+      bufsz+= sizeof(int) // type
+            + sizeof(struct Brush) // brush
+            + sizeof(int) // num_points
+            + 2*item->path->num_points*sizeof(double); // the points
+      if (item->brush.variable_width)
+        bufsz += (item->path->num_points-1)*sizeof(double); // the widths
+    }
+    else if (item->type == ITEM_TEXT) {
+      bufsz+= sizeof(int) // type
+            + sizeof(struct Brush) // brush
+            + 2*sizeof(double) // bbox upper-left
+            + sizeof(int) // text len
+            + strlen(item->text)+1 // text
+            + sizeof(int) // font_name len
+            + strlen(item->font_name)+1 // font_name
+            + sizeof(double); // font_size
+    }
+    else if (item->type == ITEM_IMAGE) {
+      if (item->image_png == NULL) {
+        set_cursor_busy(TRUE);
+        if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL))
+          item->image_png_len = 0;       // failed for some reason, so forget it
+        set_cursor_busy(FALSE);
+      }
+      bufsz+= sizeof(int) // type
+        + sizeof(struct BBox)
+        + sizeof(gsize) // png_buflen
+        + item->image_png_len;
+    }
+    else bufsz+= sizeof(int); // type
+  }
+  p = buf = g_malloc(bufsz);
+  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);
+  for (list = ui.selection->items; list != NULL; list = list->next) {
+    item = (struct Item *)list->data;
+    g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
+    if (item->type == ITEM_STROKE) {
+      g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
+      g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
+      g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
+      p+= 2*item->path->num_points*sizeof(double);
+      if (item->brush.variable_width) {
+        g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
+        p+= (item->path->num_points-1)*sizeof(double);
+      }
+    }
+    if (item->type == ITEM_TEXT) {
+      g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
+      g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
+      g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
+      val = strlen(item->text);
+      g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
+      g_memmove(p, item->text, val+1); p+= val+1;
+      val = strlen(item->font_name);
+      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 (item->type == ITEM_IMAGE) {
+      g_memmove(p, &item->bbox, sizeof(struct BBox)); p+= sizeof(struct BBox);
+      g_memmove(p, &item->image_png_len, sizeof(gsize)); p+= sizeof(gsize);
+      if (item->image_png_len > 0) {
+        g_memmove(p, item->image_png, item->image_png_len); p+= item->image_png_len;
+      }
+    }
+  }
+  
+  target.target = "_XOURNAL";
+  target.flags = 0;
+  target.info = 0;
+  
+  gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 
+       &target, 1,
+       callback_clipboard_get, callback_clipboard_clear, buf);
+}
+
+// local paste within xournal
+void clipboard_paste_from_xournal(GtkSelectionData *sel_data)
+{
+  unsigned char *p;
+  int nitems, npts, i, len;
+  struct Item *item;
+  double hoffset, voffset, cx, cy;
+  double *pf;
+  int sx, sy, wx, wy;
+  
+  reset_selection();
+  
+  ui.selection = g_new(struct Selection, 1);
+  p = sel_data->data + sizeof(int);
+  g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
+  ui.selection->type = ITEM_SELECTRECT;
+  ui.selection->layer = ui.cur_layer;
+  g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
+  ui.selection->items = NULL;
+  
+  // find by how much we translate the pasted selection
+  gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
+  gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
+  gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
+  cx -= ui.cur_page->hoffset;
+  cy -= ui.cur_page->voffset;
+  if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
+    cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
+  if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
+    cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
+  if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
+    cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
+  if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
+    cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
+  hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
+  voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
+  ui.selection->bbox.left += hoffset;
+  ui.selection->bbox.right += hoffset;
+  ui.selection->bbox.top += voffset;
+  ui.selection->bbox.bottom += voffset;
+
+  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);
+
+  while (nitems-- > 0) {
+    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++;
+    g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
+    if (item->type == ITEM_STROKE) {
+      g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
+      g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
+      item->path = gnome_canvas_points_new(npts);
+      pf = (double *)p;
+      for (i=0; i<npts; i++) {
+        item->path->coords[2*i] = pf[2*i] + hoffset;
+        item->path->coords[2*i+1] = pf[2*i+1] + voffset;
+      }
+      p+= 2*item->path->num_points*sizeof(double);
+      if (item->brush.variable_width) {
+        item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
+        p+= (item->path->num_points-1)*sizeof(double);
+      }
+      else item->widths = NULL;
+      update_item_bbox(item);
+      make_canvas_item_one(ui.cur_layer->group, item);
+    }
+    if (item->type == ITEM_TEXT) {
+      g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
+      g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
+      g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
+      item->bbox.left += hoffset;
+      item->bbox.top += voffset;
+      g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
+      item->text = g_malloc(len+1);
+      g_memmove(item->text, p, len+1); p+= len+1;
+      g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
+      item->font_name = g_malloc(len+1);
+      g_memmove(item->font_name, p, len+1); p+= len+1;
+      g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
+      make_canvas_item_one(ui.cur_layer->group, item);
+    }
+    if (item->type == ITEM_IMAGE) {
+      item->canvas_item = NULL;
+      item->image_png = NULL;
+      item->image_png_len = 0;
+      g_memmove(&item->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
+      item->bbox.left += hoffset;
+      item->bbox.right += hoffset;
+      item->bbox.top += voffset;
+      item->bbox.bottom += voffset;
+      g_memmove(&item->image_png_len, p, sizeof(gsize)); p+= sizeof(gsize);
+      if (item->image_png_len > 0) {
+        item->image_png = g_memdup(p, item->image_png_len);
+        item->image = pixbuf_from_buffer(item->image_png, item->image_png_len);
+        p+= item->image_png_len;
+      } else {
+        item->image = NULL;
+      }
+      make_canvas_item_one(ui.cur_layer->group, item);
+    }
+  }
+
+  prepare_new_undo();
+  undo->type = ITEM_PASTE;
+  undo->layer = ui.cur_layer;
+  undo->itemlist = g_list_copy(ui.selection->items);  
+  
+  gtk_selection_data_free(sel_data);
+  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)
+{
+  double pt[2];
+
+  reset_selection();
+
+  get_current_pointer_coords(pt);
+  set_current_page(pt);  
+
+  create_image_from_pixbuf(pixbuf, pt);
+}
+
+// work out what format the clipboard data is in, and paste accordingly
+void clipboard_paste(void)
+{
+  GtkSelectionData *sel_data;
+  GtkClipboard *clipboard;
+  GdkPixbuf *pixbuf;
+
+  if (ui.cur_layer == NULL) return;
+  
+  ui.cur_item_type = ITEM_PASTE;
+  clipboard = gtk_clipboard_get(GDK_SELECTION_CLIPBOARD);
+  // try xournal data
+  sel_data = gtk_clipboard_wait_for_contents(
+      clipboard,
+      gdk_atom_intern("_XOURNAL", FALSE));
+  ui.cur_item_type = ITEM_NONE;
+  if (sel_data != NULL) { 
+    clipboard_paste_from_xournal(sel_data);
+    return;
+  } 
+  // try image data
+  pixbuf = gtk_clipboard_wait_for_image(clipboard);
+  if (pixbuf != NULL) {
+    clipboard_paste_image(pixbuf);
+    return;
+  }
+}
diff --git a/src/xo-clipboard.h b/src/xo-clipboard.h
new file mode 100644 (file)
index 0000000..4007fa5
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+void selection_to_clip(void);
+void clipboard_paste(void);
index 81ce1569453295f2658c307c21bfd0344b7e87dc..129ba87873f8cf920491278c554178bfb9cccbf9 100644 (file)
@@ -43,8 +43,9 @@
 #include "xo-misc.h"
 #include "xo-file.h"
 #include "xo-paint.h"
 #include "xo-misc.h"
 #include "xo-file.h"
 #include "xo-paint.h"
+#include "xo-image.h"
 
 
-const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "", "selectrect", "vertspace", "hand"};
+const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "", "selectrect", "vertspace", "hand", "image"};
 const char *color_names[COLOR_MAX] = {"black", "blue", "red", "green",
    "gray", "lightblue", "lightgreen", "magenta", "orange", "yellow", "white"};
 const char *bgtype_names[3] = {"solid", "pixmap", "pdf"};
 const char *color_names[COLOR_MAX] = {"black", "blue", "red", "green",
    "gray", "lightblue", "lightgreen", "magenta", "orange", "yellow", "white"};
 const char *bgtype_names[3] = {"solid", "pixmap", "pdf"};
@@ -86,6 +87,47 @@ void chk_attach_names(void)
   }
 }
 
   }
 }
 
+/* Write image to file: returns true on success, false on error.
+   The image is written as a base64 encoded PNG. */
+
+gboolean write_image(gzFile f, Item *item)
+{
+  gchar *base64_str;
+
+  if (item->image_png == NULL) {
+    if (!gdk_pixbuf_save_to_buffer(item->image, &item->image_png, &item->image_png_len, "png", NULL, NULL)) {
+      item->image_png_len = 0;       // failed for some reason, so forget it
+      return FALSE;
+    }
+  }
+
+  base64_str = g_base64_encode(item->image_png, item->image_png_len);
+  gzputs(f, base64_str);
+  g_free(base64_str);
+  return TRUE;
+}
+
+// create pixbuf from base64 encoded PNG, or return NULL on failure
+
+GdkPixbuf *read_pixbuf(const gchar *base64_str, gsize base64_strlen)
+{
+  gchar *base64_str2;
+  gchar *png_buf;
+  gsize png_buflen;
+  GdkPixbuf *pixbuf;
+
+  // We have to copy the string in order to null terminate it, sigh.
+  base64_str2 = g_memdup(base64_str, base64_strlen+1);
+  base64_str2[base64_strlen] = 0;
+  png_buf = g_base64_decode(base64_str2, &png_buflen);
+
+  pixbuf = pixbuf_from_buffer(png_buf, png_buflen);
+
+  g_free(png_buf);
+  g_free(base64_str2);
+  return pixbuf;
+}
+
 // saves the journal to a file: returns true on success, false on error
 
 gboolean save_journal(const char *filename)
 // saves the journal to a file: returns true on success, false on error
 
 gboolean save_journal(const char *filename)
@@ -219,6 +261,12 @@ gboolean save_journal(const char *filename)
           gzputs(f, "</text>\n");
           g_free(tmpstr);
         }
           gzputs(f, "</text>\n");
           g_free(tmpstr);
         }
+        if (item->type == ITEM_IMAGE) {
+          gzprintf(f, "<image left=\"%.2f\" top=\"%.2f\" right=\"%.2f\" bottom=\"%.2f\">", 
+            item->bbox.left, item->bbox.top, item->bbox.right, item->bbox.bottom);
+          if (!write_image(f, item)) success = FALSE;
+          gzprintf(f, "</image>\n");
+        }
       }
       gzprintf(f, "</layer>\n");
     }
       }
       gzprintf(f, "</layer>\n");
     }
@@ -601,6 +649,56 @@ void xoj_parser_start_element(GMarkupParseContext *context,
     }
     if (has_attr!=31) *error = xoj_invalid();
   }
     }
     if (has_attr!=31) *error = xoj_invalid();
   }
+  else if (!strcmp(element_name, "image")) { // start of a image item
+    if (tmpLayer == NULL || tmpItem != NULL) {
+      *error = xoj_invalid();
+      return;
+    }
+    tmpItem = (struct Item *)g_malloc0(sizeof(struct Item));
+    tmpItem->type = ITEM_IMAGE;
+    tmpItem->canvas_item = NULL;
+    tmpItem->image=NULL;
+    tmpItem->image_png = NULL;
+    tmpItem->image_png_len = 0;
+    tmpLayer->items = g_list_append(tmpLayer->items, tmpItem);
+    tmpLayer->nitems++;
+    // scan for x, y
+    has_attr = 0;
+    while (*attribute_names!=NULL) {
+      if (!strcmp(*attribute_names, "left")) {
+        if (has_attr & 1) *error = xoj_invalid();
+        cleanup_numeric((gchar *)*attribute_values);
+        tmpItem->bbox.left = g_ascii_strtod(*attribute_values, &ptr);
+        if (ptr == *attribute_values) *error = xoj_invalid();
+        has_attr |= 1;
+      }
+      else if (!strcmp(*attribute_names, "top")) {
+        if (has_attr & 2) *error = xoj_invalid();
+        cleanup_numeric((gchar *)*attribute_values);
+        tmpItem->bbox.top = g_ascii_strtod(*attribute_values, &ptr);
+        if (ptr == *attribute_values) *error = xoj_invalid();
+        has_attr |= 2;
+      }
+      else if (!strcmp(*attribute_names, "right")) {
+        if (has_attr & 4) *error = xoj_invalid();
+        cleanup_numeric((gchar *)*attribute_values);
+        tmpItem->bbox.right = g_ascii_strtod(*attribute_values, &ptr);
+        if (ptr == *attribute_values) *error = xoj_invalid();
+        has_attr |= 4;
+      }
+      else if (!strcmp(*attribute_names, "bottom")) {
+        if (has_attr & 8) *error = xoj_invalid();
+        cleanup_numeric((gchar *)*attribute_values);
+        tmpItem->bbox.bottom = g_ascii_strtod(*attribute_values, &ptr);
+        if (ptr == *attribute_values) *error = xoj_invalid();
+        has_attr |= 8;
+      }
+      else *error = xoj_invalid();
+      attribute_names++;
+      attribute_values++;
+    }
+    if (has_attr!=15) *error = xoj_invalid();
+  }
 }
 
 void xoj_parser_end_element(GMarkupParseContext *context,
 }
 
 void xoj_parser_end_element(GMarkupParseContext *context,
@@ -636,6 +734,13 @@ void xoj_parser_end_element(GMarkupParseContext *context,
     }
     tmpItem = NULL;
   }
     }
     tmpItem = NULL;
   }
+  if (!strcmp(element_name, "image")) {
+    if (tmpItem == NULL) {
+      *error = xoj_invalid();
+      return;
+    }
+    tmpItem = NULL;
+  }
 }
 
 void xoj_parser_text(GMarkupParseContext *context,
 }
 
 void xoj_parser_text(GMarkupParseContext *context,
@@ -673,6 +778,9 @@ void xoj_parser_text(GMarkupParseContext *context,
     g_memmove(tmpItem->text, text, text_len);
     tmpItem->text[text_len]=0;
   }
     g_memmove(tmpItem->text, text, text_len);
     tmpItem->text[text_len]=0;
   }
+  if (!strcmp(element_name, "image")) {
+    tmpItem->image = read_pixbuf(text, text_len);
+  }
 }
 
 gboolean user_wants_second_chance(char **filename)
 }
 
 gboolean user_wants_second_chance(char **filename)
@@ -1386,6 +1494,7 @@ void init_config_default(void)
   ui.print_ruling = TRUE;
   ui.default_unit = UNIT_CM;
   ui.default_path = NULL;
   ui.print_ruling = TRUE;
   ui.default_unit = UNIT_CM;
   ui.default_path = NULL;
+  ui.default_image = NULL;
   ui.default_font_name = g_strdup(DEFAULT_FONT);
   ui.default_font_size = DEFAULT_FONT_SIZE;
   ui.pressure_sensitivity = FALSE;
   ui.default_font_name = g_strdup(DEFAULT_FONT);
   ui.default_font_size = DEFAULT_FONT_SIZE;
   ui.pressure_sensitivity = FALSE;
diff --git a/src/xo-image.c b/src/xo-image.c
new file mode 100644 (file)
index 0000000..884bf23
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include <config.h>
+#endif
+
+#include <math.h>
+#include <string.h>
+#include <gtk/gtk.h>
+#include <gio/gio.h>
+
+#include "xournal.h"
+#include "xo-support.h"
+#include "xo-image.h"
+
+// create pixbuf from buffer, or return NULL on failure
+GdkPixbuf *pixbuf_from_buffer(const gchar *buf, gsize buflen)
+{
+  GInputStream *istream;
+  GdkPixbuf *pixbuf;
+  GError *error;
+
+  error = NULL;
+
+  istream = g_memory_input_stream_new_from_data (buf, buflen, NULL);
+  pixbuf = gdk_pixbuf_new_from_stream(istream, NULL, &error); 
+  g_input_stream_close(istream, NULL, &error);
+  return pixbuf;
+}
+
+void create_image_from_pixbuf(GdkPixbuf *pixbuf, double *pt)
+{
+  double scale;
+  struct Item *item;
+
+  item = g_new(struct Item, 1);
+  item->type = ITEM_IMAGE;
+  item->canvas_item = NULL;
+  item->bbox.left = pt[0];
+  item->bbox.top = pt[1];
+  item->image = pixbuf;
+  item->image_png = NULL;
+  item->image_png_len = 0;
+
+  // Scale at native size, unless that won't fit, in which case we shrink it down.
+  scale = 1 / ui.zoom;
+  if ((scale * gdk_pixbuf_get_width(item->image)) > ui.cur_page->width - item->bbox.left)
+    scale = (ui.cur_page->width - item->bbox.left) / gdk_pixbuf_get_width(item->image);
+  if ((scale * gdk_pixbuf_get_height(item->image)) > ui.cur_page->height - item->bbox.top)
+    scale = (ui.cur_page->height - item->bbox.top) / gdk_pixbuf_get_height(item->image);
+
+  item->bbox.right = item->bbox.left + scale * gdk_pixbuf_get_width(item->image);
+  item->bbox.bottom = item->bbox.top + scale * gdk_pixbuf_get_height(item->image);
+  ui.cur_layer->items = g_list_append(ui.cur_layer->items, item);
+  ui.cur_layer->nitems++;
+  
+  make_canvas_item_one(ui.cur_layer->group, item);
+
+  // add undo information
+  prepare_new_undo();
+  undo->type = ITEM_IMAGE;
+  undo->item = item;
+  undo->layer = ui.cur_layer;
+  ui.cur_item = NULL;
+  ui.cur_item_type = ITEM_NONE;
+
+  // select image
+  reset_selection();
+  ui.selection = g_new0(struct Selection, 1);
+  ui.selection->type = ITEM_SELECTRECT;
+  ui.selection->layer = ui.cur_layer;
+  ui.selection->bbox = item->bbox;
+  ui.selection->items = g_list_append(ui.selection->items, item);
+  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);
+  update_copy_paste_enabled();
+}
+
+void insert_image(GdkEvent *event)
+{
+  GtkTextBuffer *buffer;
+  GnomeCanvasItem *canvas_item;
+  GdkColor color;
+  GtkWidget *dialog;
+  GtkFileFilter *filt_all;
+  GtkFileFilter *filt_gdkimage;
+  char *filename;
+  GdkPixbuf *pixbuf;
+  double scale=1;
+  double pt[2];
+  
+  dialog = gtk_file_chooser_dialog_new(_("Insert Image"), GTK_WINDOW (winMain),
+     GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
+     GTK_STOCK_OPEN, GTK_RESPONSE_OK, NULL);
+#ifdef FILE_DIALOG_SIZE_BUGFIX
+  gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 400);
+#endif
+     
+  filt_all = gtk_file_filter_new();
+  gtk_file_filter_set_name(filt_all, _("All files"));
+  gtk_file_filter_add_pattern(filt_all, "*");
+  filt_gdkimage = gtk_file_filter_new();
+  gtk_file_filter_set_name(filt_gdkimage, _("Image files"));
+  gtk_file_filter_add_pixbuf_formats(filt_gdkimage);
+  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_gdkimage);
+  gtk_file_chooser_add_filter(GTK_FILE_CHOOSER (dialog), filt_all);
+
+  if (ui.default_image != NULL) gtk_file_chooser_set_filename(GTK_FILE_CHOOSER (dialog), ui.default_image);
+
+  if (gtk_dialog_run(GTK_DIALOG(dialog)) != GTK_RESPONSE_OK) {
+    gtk_widget_destroy(dialog);
+    return;
+  }
+  filename = gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (dialog));
+  gtk_widget_destroy(dialog);
+
+  if (filename == NULL) return; /* nothing selected */
+
+  if (ui.default_image != NULL) g_free(ui.default_image);
+  ui.default_image = g_strdup(filename);
+  
+  set_cursor_busy(TRUE);
+  pixbuf=gdk_pixbuf_new_from_file(filename, NULL);
+  set_cursor_busy(FALSE);
+  
+  if(pixbuf==NULL) { /* open failed */
+    dialog = gtk_message_dialog_new(GTK_WINDOW (winMain), GTK_DIALOG_MODAL,
+      GTK_MESSAGE_ERROR, GTK_BUTTONS_OK, _("Error opening image '%s'"), filename);
+    gtk_dialog_run(GTK_DIALOG(dialog));
+    gtk_widget_destroy(dialog);
+    g_free(filename);
+    ui.cur_item = NULL;
+    ui.cur_item_type = ITEM_NONE;
+    return;
+  }
+
+  ui.cur_item_type = ITEM_IMAGE;
+
+  get_pointer_coords(event, pt);
+  set_current_page(pt);  
+
+  create_image_from_pixbuf(pixbuf, pt);
+}
+
+void rescale_images(void)
+{
+  // nothing needed in this implementation
+}
+
diff --git a/src/xo-image.h b/src/xo-image.h
new file mode 100644 (file)
index 0000000..e0cb8fa
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This software is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of  
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+GdkPixbuf *pixbuf_from_buffer(const gchar *buf, gsize buflen);
+void create_image_from_pixbuf(GdkPixbuf *pixbuf, double *pt);
+void insert_image(GdkEvent *event);
+void rescale_images(void);
index f6022eaccc314bd395d5963f5251500597dac383..00a72d1780f0ebb06a7c6e95bdd895aa60d99ff4 100644 (file)
@@ -143,6 +143,7 @@ create_winMain (void)
   GtkWidget *toolsEraser;
   GtkWidget *toolsHighlighter;
   GtkWidget *toolsText;
   GtkWidget *toolsEraser;
   GtkWidget *toolsHighlighter;
   GtkWidget *toolsText;
+  GtkWidget *toolsImage;
   GtkWidget *separator15;
   GtkWidget *toolsReco;
   GtkWidget *toolsRuler;
   GtkWidget *separator15;
   GtkWidget *toolsReco;
   GtkWidget *toolsRuler;
@@ -215,6 +216,7 @@ create_winMain (void)
   GtkWidget *button2Eraser;
   GtkWidget *button2Highlighter;
   GtkWidget *button2Text;
   GtkWidget *button2Eraser;
   GtkWidget *button2Highlighter;
   GtkWidget *button2Text;
+  GtkWidget *button2Image;
   GtkWidget *button2SelectRegion;
   GtkWidget *button2SelectRectangle;
   GtkWidget *button2VerticalSpace;
   GtkWidget *button2SelectRegion;
   GtkWidget *button2SelectRectangle;
   GtkWidget *button2VerticalSpace;
@@ -231,6 +233,7 @@ create_winMain (void)
   GtkWidget *button3Eraser;
   GtkWidget *button3Highlighter;
   GtkWidget *button3Text;
   GtkWidget *button3Eraser;
   GtkWidget *button3Highlighter;
   GtkWidget *button3Text;
+  GtkWidget *button3Image;
   GtkWidget *button3SelectRegion;
   GtkWidget *button3SelectRectangle;
   GtkWidget *button3VerticalSpace;
   GtkWidget *button3SelectRegion;
   GtkWidget *button3SelectRectangle;
   GtkWidget *button3VerticalSpace;
@@ -289,6 +292,7 @@ create_winMain (void)
   GtkWidget *buttonEraser;
   GtkWidget *buttonHighlighter;
   GtkWidget *buttonText;
   GtkWidget *buttonEraser;
   GtkWidget *buttonHighlighter;
   GtkWidget *buttonText;
+  GtkWidget *buttonImage;
   GtkWidget *buttonReco;
   GtkWidget *buttonRuler;
   GtkWidget *toolitem15;
   GtkWidget *buttonReco;
   GtkWidget *buttonRuler;
   GtkWidget *toolitem15;
@@ -662,7 +666,7 @@ create_winMain (void)
   gtk_widget_show (image631);
   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (viewHideLayer), image631);
 
   gtk_widget_show (image631);
   gtk_image_menu_item_set_image (GTK_IMAGE_MENU_ITEM (viewHideLayer), image631);
 
-  menuJournal = gtk_menu_item_new_with_mnemonic (_("_Journal"));
+  menuJournal = gtk_menu_item_new_with_mnemonic (_("_Page"));
   gtk_widget_show (menuJournal);
   gtk_container_add (GTK_CONTAINER (menubar), menuJournal);
 
   gtk_widget_show (menuJournal);
   gtk_container_add (GTK_CONTAINER (menubar), menuJournal);
 
@@ -876,6 +880,15 @@ create_winMain (void)
                               GTK_ACCEL_VISIBLE);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (toolsText), TRUE);
 
                               GTK_ACCEL_VISIBLE);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (toolsText), TRUE);
 
+  toolsImage = gtk_radio_menu_item_new_with_mnemonic (toolsPen_group, _("_Image"));
+  toolsPen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (toolsImage));
+  gtk_widget_show (toolsImage);
+  gtk_container_add (GTK_CONTAINER (menuTools_menu), toolsImage);
+  gtk_widget_add_accelerator (toolsImage, "activate", accel_group,
+                              GDK_I, (GdkModifierType) GDK_CONTROL_MASK | GDK_SHIFT_MASK,
+                              GTK_ACCEL_VISIBLE);
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (toolsImage), TRUE);
+
   separator15 = gtk_separator_menu_item_new ();
   gtk_widget_show (separator15);
   gtk_container_add (GTK_CONTAINER (menuTools_menu), separator15);
   separator15 = gtk_separator_menu_item_new ();
   gtk_widget_show (separator15);
   gtk_container_add (GTK_CONTAINER (menuTools_menu), separator15);
@@ -1227,6 +1240,12 @@ create_winMain (void)
   gtk_container_add (GTK_CONTAINER (button2_mapping_menu), button2Text);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button2Text), TRUE);
 
   gtk_container_add (GTK_CONTAINER (button2_mapping_menu), button2Text);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button2Text), TRUE);
 
+  button2Image = gtk_radio_menu_item_new_with_mnemonic (button2Pen_group, _("_Image"));
+  button2Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button2Image));
+  gtk_widget_show (button2Image);
+  gtk_container_add (GTK_CONTAINER (button2_mapping_menu), button2Image);
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button2Image), TRUE);
+
   button2SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button2Pen_group, _("Select Re_gion"));
   button2Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button2SelectRegion));
   gtk_widget_show (button2SelectRegion);
   button2SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button2Pen_group, _("Select Re_gion"));
   button2Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button2SelectRegion));
   gtk_widget_show (button2SelectRegion);
@@ -1303,6 +1322,12 @@ create_winMain (void)
   gtk_container_add (GTK_CONTAINER (button3_mapping_menu), button3Text);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button3Text), TRUE);
 
   gtk_container_add (GTK_CONTAINER (button3_mapping_menu), button3Text);
   gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button3Text), TRUE);
 
+  button3Image = gtk_radio_menu_item_new_with_mnemonic (button3Pen_group, _("_Image"));
+  button3Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button3Image));
+  gtk_widget_show (button3Image);
+  gtk_container_add (GTK_CONTAINER (button3_mapping_menu), button3Image);
+  gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (button3Image), TRUE);
+
   button3SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button3Pen_group, _("Select Re_gion"));
   button3Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button3SelectRegion));
   gtk_widget_show (button3SelectRegion);
   button3SelectRegion = gtk_radio_menu_item_new_with_mnemonic (button3Pen_group, _("Select Re_gion"));
   button3Pen_group = gtk_radio_menu_item_get_group (GTK_RADIO_MENU_ITEM (button3SelectRegion));
   gtk_widget_show (button3SelectRegion);
@@ -1588,6 +1613,17 @@ create_winMain (void)
   gtk_radio_tool_button_set_group (GTK_RADIO_TOOL_BUTTON (buttonText), buttonPen_group);
   buttonPen_group = gtk_radio_tool_button_get_group (GTK_RADIO_TOOL_BUTTON (buttonText));
 
   gtk_radio_tool_button_set_group (GTK_RADIO_TOOL_BUTTON (buttonText), buttonPen_group);
   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"));
+  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_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));
+
   buttonReco = (GtkWidget*) gtk_toggle_tool_button_new ();
   gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonReco), _("Shape Recognizer"));
   tmp_image = create_pixmap (winMain, "shapes.png");
   buttonReco = (GtkWidget*) gtk_toggle_tool_button_new ();
   gtk_tool_button_set_label (GTK_TOOL_BUTTON (buttonReco), _("Shape Recognizer"));
   tmp_image = create_pixmap (winMain, "shapes.png");
@@ -2147,6 +2183,9 @@ create_winMain (void)
   g_signal_connect ((gpointer) toolsText, "toggled",
                     G_CALLBACK (on_toolsText_activate),
                     NULL);
   g_signal_connect ((gpointer) toolsText, "toggled",
                     G_CALLBACK (on_toolsText_activate),
                     NULL);
+  g_signal_connect ((gpointer) toolsImage, "toggled",
+                    G_CALLBACK (on_toolsImage_activate),
+                    NULL);
   g_signal_connect ((gpointer) toolsReco, "toggled",
                     G_CALLBACK (on_toolsReco_activate),
                     NULL);
   g_signal_connect ((gpointer) toolsReco, "toggled",
                     G_CALLBACK (on_toolsReco_activate),
                     NULL);
@@ -2282,6 +2321,9 @@ create_winMain (void)
   g_signal_connect ((gpointer) button2Text, "activate",
                     G_CALLBACK (on_button2Text_activate),
                     NULL);
   g_signal_connect ((gpointer) button2Text, "activate",
                     G_CALLBACK (on_button2Text_activate),
                     NULL);
+  g_signal_connect ((gpointer) button2Image, "activate",
+                    G_CALLBACK (on_button2Image_activate),
+                    NULL);
   g_signal_connect ((gpointer) button2SelectRegion, "activate",
                     G_CALLBACK (on_button2SelectRegion_activate),
                     NULL);
   g_signal_connect ((gpointer) button2SelectRegion, "activate",
                     G_CALLBACK (on_button2SelectRegion_activate),
                     NULL);
@@ -2312,6 +2354,9 @@ create_winMain (void)
   g_signal_connect ((gpointer) button3Text, "activate",
                     G_CALLBACK (on_button3Text_activate),
                     NULL);
   g_signal_connect ((gpointer) button3Text, "activate",
                     G_CALLBACK (on_button3Text_activate),
                     NULL);
+  g_signal_connect ((gpointer) button3Image, "activate",
+                    G_CALLBACK (on_button3Image_activate),
+                    NULL);
   g_signal_connect ((gpointer) button3SelectRegion, "activate",
                     G_CALLBACK (on_button3SelectRegion_activate),
                     NULL);
   g_signal_connect ((gpointer) button3SelectRegion, "activate",
                     G_CALLBACK (on_button3SelectRegion_activate),
                     NULL);
@@ -2426,6 +2471,9 @@ create_winMain (void)
   g_signal_connect ((gpointer) buttonText, "toggled",
                     G_CALLBACK (on_toolsText_activate),
                     NULL);
   g_signal_connect ((gpointer) buttonText, "toggled",
                     G_CALLBACK (on_toolsText_activate),
                     NULL);
+  g_signal_connect ((gpointer) buttonImage, "toggled",
+                    G_CALLBACK (on_toolsImage_activate),
+                    NULL);
   g_signal_connect ((gpointer) buttonReco, "toggled",
                     G_CALLBACK (on_toolsReco_activate),
                     NULL);
   g_signal_connect ((gpointer) buttonReco, "toggled",
                     G_CALLBACK (on_toolsReco_activate),
                     NULL);
@@ -2616,6 +2664,7 @@ create_winMain (void)
   GLADE_HOOKUP_OBJECT (winMain, toolsEraser, "toolsEraser");
   GLADE_HOOKUP_OBJECT (winMain, toolsHighlighter, "toolsHighlighter");
   GLADE_HOOKUP_OBJECT (winMain, toolsText, "toolsText");
   GLADE_HOOKUP_OBJECT (winMain, toolsEraser, "toolsEraser");
   GLADE_HOOKUP_OBJECT (winMain, toolsHighlighter, "toolsHighlighter");
   GLADE_HOOKUP_OBJECT (winMain, toolsText, "toolsText");
+  GLADE_HOOKUP_OBJECT (winMain, toolsImage, "toolsImage");
   GLADE_HOOKUP_OBJECT (winMain, separator15, "separator15");
   GLADE_HOOKUP_OBJECT (winMain, toolsReco, "toolsReco");
   GLADE_HOOKUP_OBJECT (winMain, toolsRuler, "toolsRuler");
   GLADE_HOOKUP_OBJECT (winMain, separator15, "separator15");
   GLADE_HOOKUP_OBJECT (winMain, toolsReco, "toolsReco");
   GLADE_HOOKUP_OBJECT (winMain, toolsRuler, "toolsRuler");
@@ -2682,6 +2731,7 @@ create_winMain (void)
   GLADE_HOOKUP_OBJECT (winMain, button2Eraser, "button2Eraser");
   GLADE_HOOKUP_OBJECT (winMain, button2Highlighter, "button2Highlighter");
   GLADE_HOOKUP_OBJECT (winMain, button2Text, "button2Text");
   GLADE_HOOKUP_OBJECT (winMain, button2Eraser, "button2Eraser");
   GLADE_HOOKUP_OBJECT (winMain, button2Highlighter, "button2Highlighter");
   GLADE_HOOKUP_OBJECT (winMain, button2Text, "button2Text");
+  GLADE_HOOKUP_OBJECT (winMain, button2Image, "button2Image");
   GLADE_HOOKUP_OBJECT (winMain, button2SelectRegion, "button2SelectRegion");
   GLADE_HOOKUP_OBJECT (winMain, button2SelectRectangle, "button2SelectRectangle");
   GLADE_HOOKUP_OBJECT (winMain, button2VerticalSpace, "button2VerticalSpace");
   GLADE_HOOKUP_OBJECT (winMain, button2SelectRegion, "button2SelectRegion");
   GLADE_HOOKUP_OBJECT (winMain, button2SelectRectangle, "button2SelectRectangle");
   GLADE_HOOKUP_OBJECT (winMain, button2VerticalSpace, "button2VerticalSpace");
@@ -2696,6 +2746,7 @@ create_winMain (void)
   GLADE_HOOKUP_OBJECT (winMain, button3Eraser, "button3Eraser");
   GLADE_HOOKUP_OBJECT (winMain, button3Highlighter, "button3Highlighter");
   GLADE_HOOKUP_OBJECT (winMain, button3Text, "button3Text");
   GLADE_HOOKUP_OBJECT (winMain, button3Eraser, "button3Eraser");
   GLADE_HOOKUP_OBJECT (winMain, button3Highlighter, "button3Highlighter");
   GLADE_HOOKUP_OBJECT (winMain, button3Text, "button3Text");
+  GLADE_HOOKUP_OBJECT (winMain, button3Image, "button3Image");
   GLADE_HOOKUP_OBJECT (winMain, button3SelectRegion, "button3SelectRegion");
   GLADE_HOOKUP_OBJECT (winMain, button3SelectRectangle, "button3SelectRectangle");
   GLADE_HOOKUP_OBJECT (winMain, button3VerticalSpace, "button3VerticalSpace");
   GLADE_HOOKUP_OBJECT (winMain, button3SelectRegion, "button3SelectRegion");
   GLADE_HOOKUP_OBJECT (winMain, button3SelectRectangle, "button3SelectRectangle");
   GLADE_HOOKUP_OBJECT (winMain, button3VerticalSpace, "button3VerticalSpace");
@@ -2750,6 +2801,7 @@ create_winMain (void)
   GLADE_HOOKUP_OBJECT (winMain, buttonEraser, "buttonEraser");
   GLADE_HOOKUP_OBJECT (winMain, buttonHighlighter, "buttonHighlighter");
   GLADE_HOOKUP_OBJECT (winMain, buttonText, "buttonText");
   GLADE_HOOKUP_OBJECT (winMain, buttonEraser, "buttonEraser");
   GLADE_HOOKUP_OBJECT (winMain, buttonHighlighter, "buttonHighlighter");
   GLADE_HOOKUP_OBJECT (winMain, buttonText, "buttonText");
+  GLADE_HOOKUP_OBJECT (winMain, buttonImage, "buttonImage");
   GLADE_HOOKUP_OBJECT (winMain, buttonReco, "buttonReco");
   GLADE_HOOKUP_OBJECT (winMain, buttonRuler, "buttonRuler");
   GLADE_HOOKUP_OBJECT (winMain, toolitem15, "toolitem15");
   GLADE_HOOKUP_OBJECT (winMain, buttonReco, "buttonReco");
   GLADE_HOOKUP_OBJECT (winMain, buttonRuler, "buttonRuler");
   GLADE_HOOKUP_OBJECT (winMain, toolitem15, "toolitem15");
index bc4747cd35e28d75c26f1738610c599e13fada41..bd65f1afbede9a8a3a9cde569a3c0a197a1cb581 100644 (file)
@@ -31,6 +31,7 @@
 #include "xo-file.h"
 #include "xo-paint.h"
 #include "xo-shapes.h"
 #include "xo-file.h"
 #include "xo-paint.h"
 #include "xo-shapes.h"
+#include "xo-image.h"
 
 // some global constants
 
 
 // some global constants
 
@@ -104,6 +105,31 @@ struct Page *new_page_with_bg(struct Background *bg, double width, double height
   return pg;
 }
 
   return pg;
 }
 
+// change the current page if necessary for pointer at pt
+void set_current_page(gdouble *pt)
+{
+  gboolean page_change;
+  struct Page *tmppage;
+
+  page_change = FALSE;
+  tmppage = ui.cur_page;
+  while (ui.view_continuous && (pt[1] < - VIEW_CONTINUOUS_SKIP)) {
+    if (ui.pageno == 0) break;
+    page_change = TRUE;
+    ui.pageno--;
+    tmppage = g_list_nth_data(journal.pages, ui.pageno);
+    pt[1] += tmppage->height + VIEW_CONTINUOUS_SKIP;
+  }
+  while (ui.view_continuous && (pt[1] > tmppage->height + VIEW_CONTINUOUS_SKIP)) {
+    if (ui.pageno == journal.npages-1) break;
+    pt[1] -= tmppage->height + VIEW_CONTINUOUS_SKIP;
+    page_change = TRUE;
+    ui.pageno++;
+    tmppage = g_list_nth_data(journal.pages, ui.pageno);
+  }
+  if (page_change) do_switch_page(ui.pageno, FALSE, FALSE);
+}
+
 void realloc_cur_path(int n)
 {
   if (n <= ui.cur_path_storage_alloc) return;
 void realloc_cur_path(int n)
 {
   if (n <= ui.cur_path_storage_alloc) return;
@@ -156,6 +182,11 @@ void clear_redo_stack(void)
       g_free(redo->item->font_name);
       g_free(redo->item);
     }
       g_free(redo->item->font_name);
       g_free(redo->item);
     }
+    else if (redo->type == ITEM_IMAGE) {
+      g_object_unref(redo->item->image);
+      g_free(redo->item->image_png);
+      g_free(redo->item);
+    }
     else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
       for (list = redo->erasurelist; list!=NULL; list=list->next) {
         erasure = (struct UndoErasureData *)list->data;
     else if (redo->type == ITEM_ERASURE || redo->type == ITEM_RECOGNIZER) {
       for (list = redo->erasurelist; list!=NULL; list=list->next) {
         erasure = (struct UndoErasureData *)list->data;
@@ -232,6 +263,10 @@ void clear_undo_stack(void)
         }
         if (erasure->item->type == ITEM_TEXT)
           { g_free(erasure->item->text); g_free(erasure->item->font_name); }
         }
         if (erasure->item->type == ITEM_TEXT)
           { g_free(erasure->item->text); g_free(erasure->item->font_name); }
+        if (erasure->item->type == ITEM_IMAGE) {
+          g_object_unref(erasure->item->image);
+          g_free(erasure->item->image_png);
+        }
         g_free(erasure->item);
         g_list_free(erasure->replacement_items);
         g_free(erasure);
         g_free(erasure->item);
         g_list_free(erasure->replacement_items);
         g_free(erasure);
@@ -316,6 +351,10 @@ void delete_layer(struct Layer *l)
     if (item->type == ITEM_TEXT) {
       g_free(item->font_name); g_free(item->text);
     }
     if (item->type == ITEM_TEXT) {
       g_free(item->font_name); g_free(item->text);
     }
+    if (item->type == ITEM_IMAGE) {
+      g_object_unref(item->image);
+      g_free(item->image_png);
+    }
     // don't need to delete the canvas_item, as it's part of the group destroyed below
     g_free(item);
     l->items = g_list_delete_link(l->items, l->items);
     // don't need to delete the canvas_item, as it's part of the group destroyed below
     g_free(item);
     l->items = g_list_delete_link(l->items, l->items);
@@ -370,6 +409,17 @@ void get_pointer_coords(GdkEvent *event, gdouble *ret)
   ret[1] -= ui.cur_page->voffset;
 }
 
   ret[1] -= ui.cur_page->voffset;
 }
 
+void get_current_pointer_coords(gdouble *ret)
+{
+  gint wx, wy, sx, sy;
+
+  gtk_widget_get_pointer((GtkWidget *)canvas, &wx, &wy);
+  gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
+  gnome_canvas_window_to_world(canvas, (double)(wx + sx), (double)(wy + sy), ret, ret+1);
+  ret[0] -= ui.cur_page->hoffset;
+  ret[1] -= ui.cur_page->voffset;
+}
+
 void fix_xinput_coords(GdkEvent *event)
 {
   double *axes, *px, *py, axis_width;
 void fix_xinput_coords(GdkEvent *event)
 {
   double *axes, *px, *py, axis_width;
@@ -533,6 +583,16 @@ void make_canvas_item_one(GnomeCanvasGroup *group, struct Item *item)
           "text", item->text, NULL);
     update_item_bbox(item);
   }
           "text", item->text, NULL);
     update_item_bbox(item);
   }
+  if (item->type == ITEM_IMAGE) {
+    item->canvas_item = gnome_canvas_item_new(group,
+          gnome_canvas_pixbuf_get_type(),
+          "pixbuf", item->image,
+          "x", item->bbox.left, "y", item->bbox.top,
+          "width", item->bbox.right - item->bbox.left,
+          "height", item->bbox.bottom - item->bbox.top,
+          "width-set", TRUE, "height-set", TRUE,
+          NULL);
+  }
 }
 
 void make_canvas_items(void)
 }
 
 void make_canvas_items(void)
@@ -881,6 +941,10 @@ void update_tool_buttons(void)
       gtk_toggle_tool_button_set_active(
         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
       break;
       gtk_toggle_tool_button_set_active(
         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonText")), TRUE);
       break;
+    case TOOL_IMAGE:
+      gtk_toggle_tool_button_set_active(
+        GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonImage")), TRUE);
+      break;
     case TOOL_SELECTREGION:
       gtk_toggle_tool_button_set_active(
         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
     case TOOL_SELECTREGION:
       gtk_toggle_tool_button_set_active(
         GTK_TOGGLE_TOOL_BUTTON(GET_COMPONENT("buttonSelectRegion")), TRUE);
@@ -929,6 +993,10 @@ void update_tool_menu(void)
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
       break;
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsText")), TRUE);
       break;
+    case TOOL_IMAGE:
+      gtk_check_menu_item_set_active(
+        GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsImage")), TRUE);
+      break;
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("toolsSelectRegion")), TRUE);
@@ -1159,6 +1227,10 @@ void update_mappings_menu(void)
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
       break;
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Text")), TRUE);
       break;
+    case TOOL_IMAGE:
+      gtk_check_menu_item_set_active(
+        GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2Image")), TRUE);
+      break;
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button2SelectRegion")), TRUE);
@@ -1189,6 +1261,10 @@ void update_mappings_menu(void)
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
       break;
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Text")), TRUE);
       break;
+    case TOOL_IMAGE:
+      gtk_check_menu_item_set_active(
+        GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3Image")), TRUE);
+      break;
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
     case TOOL_SELECTREGION:
       gtk_check_menu_item_set_active(
         GTK_CHECK_MENU_ITEM(GET_COMPONENT("button3SelectRegion")), TRUE);
@@ -1743,7 +1819,8 @@ void move_journal_items_by(GList *itemlist, double dx, double dy,
     if (item->type == ITEM_STROKE)
       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
         { pt[0] += dx; pt[1] += dy; }
     if (item->type == ITEM_STROKE)
       for (pt=item->path->coords, i=0; i<item->path->num_points; i++, pt+=2)
         { pt[0] += dx; pt[1] += dy; }
-    if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || item->type == ITEM_TEMP_TEXT) {
+    if (item->type == ITEM_STROKE || item->type == ITEM_TEXT || 
+        item->type == ITEM_TEMP_TEXT || item->type == ITEM_IMAGE) {
       item->bbox.left += dx;
       item->bbox.right += dx;
       item->bbox.top += dy;
       item->bbox.left += dx;
       item->bbox.right += dx;
       item->bbox.top += dy;
@@ -1826,6 +1903,22 @@ void resize_journal_items_by(GList *itemlist, double scaling_x, double scaling_y
       item->bbox.left = item->bbox.left*scaling_x + offset_x;
       item->bbox.top = item->bbox.top*scaling_y + offset_y;
     }
       item->bbox.left = item->bbox.left*scaling_x + offset_x;
       item->bbox.top = item->bbox.top*scaling_y + offset_y;
     }
+    if (item->type == ITEM_IMAGE) {
+      item->bbox.left = item->bbox.left*scaling_x + offset_x;
+      item->bbox.right = item->bbox.right*scaling_x + offset_x;
+      item->bbox.top = item->bbox.top*scaling_y + offset_y;
+      item->bbox.bottom = item->bbox.bottom*scaling_y + offset_y;
+      if (item->bbox.left > item->bbox.right) {
+        temp = item->bbox.left;
+        item->bbox.left = item->bbox.right;
+        item->bbox.right = temp;
+      }
+      if (item->bbox.top > item->bbox.bottom) {
+        temp = item->bbox.top;
+        item->bbox.top = item->bbox.bottom;
+        item->bbox.bottom = temp;
+      }
+    }
     // redraw the item
     if (item->canvas_item!=NULL) {
       group = (GnomeCanvasGroup *) item->canvas_item->parent;
     // redraw the item
     if (item->canvas_item!=NULL) {
       group = (GnomeCanvasGroup *) item->canvas_item->parent;
index da1433dcfdae007ac51bcf5722cfcb1410c53f4d..617bbda92da29dcae0cffdf02b9f8f15b281c68d 100644 (file)
@@ -17,6 +17,7 @@
 
 struct Page *new_page(struct Page *template);
 struct Page *new_page_with_bg(struct Background *bg, double width, double height);
 
 struct Page *new_page(struct Page *template);
 struct Page *new_page_with_bg(struct Background *bg, double width, double height);
+void set_current_page(gdouble *pt);
 void realloc_cur_path(int n);
 void realloc_cur_widths(int n);
 void clear_redo_stack(void);
 void realloc_cur_path(int n);
 void realloc_cur_widths(int n);
 void clear_redo_stack(void);
@@ -36,6 +37,7 @@ void refstring_unref(struct Refstring *rs);
 
 int finite_sized(double x);
 void get_pointer_coords(GdkEvent *event, double *ret);
 
 int finite_sized(double x);
 void get_pointer_coords(GdkEvent *event, double *ret);
+void get_current_pointer_coords(double *ret);
 double get_pressure_multiplier(GdkEvent *event);
 void fix_xinput_coords(GdkEvent *event);
 void update_item_bbox(struct Item *item);
 double get_pressure_multiplier(GdkEvent *event);
 void fix_xinput_coords(GdkEvent *event);
 void update_item_bbox(struct Item *item);
index c777482546691566c085be2dba87571d7be913c3..33576f8a21cca3fc5ccbef072e631fb5d9f4e361 100644 (file)
@@ -964,211 +964,6 @@ void selection_delete(void)
      the forward direction */
 }
 
      the forward direction */
 }
 
-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);
-}
-
-void callback_clipboard_clear(GtkClipboard *clipboard, gpointer user_data)
-{
-  g_free(user_data);
-}
-
-void selection_to_clip(void)
-{
-  int bufsz, nitems, val;
-  char *buf, *p;
-  GList *list;
-  struct Item *item;
-  GtkTargetEntry target;
-  
-  if (ui.selection == NULL) return;
-  bufsz = 2*sizeof(int) // bufsz, nitems
-        + sizeof(struct BBox); // bbox
-  nitems = 0;
-  for (list = ui.selection->items; list != NULL; list = list->next) {
-    item = (struct Item *)list->data;
-    nitems++;
-    if (item->type == ITEM_STROKE) {
-      bufsz+= sizeof(int) // type
-            + sizeof(struct Brush) // brush
-            + sizeof(int) // num_points
-            + 2*item->path->num_points*sizeof(double); // the points
-      if (item->brush.variable_width)
-        bufsz += (item->path->num_points-1)*sizeof(double); // the widths
-    }
-    else if (item->type == ITEM_TEXT) {
-      bufsz+= sizeof(int) // type
-            + sizeof(struct Brush) // brush
-            + 2*sizeof(double) // bbox upper-left
-            + sizeof(int) // text len
-            + strlen(item->text)+1 // text
-            + sizeof(int) // font_name len
-            + strlen(item->font_name)+1 // font_name
-            + sizeof(double); // font_size
-    }
-    else bufsz+= sizeof(int); // type
-  }
-  p = buf = g_malloc(bufsz);
-  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);
-  for (list = ui.selection->items; list != NULL; list = list->next) {
-    item = (struct Item *)list->data;
-    g_memmove(p, &item->type, sizeof(int)); p+= sizeof(int);
-    if (item->type == ITEM_STROKE) {
-      g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
-      g_memmove(p, &item->path->num_points, sizeof(int)); p+= sizeof(int);
-      g_memmove(p, item->path->coords, 2*item->path->num_points*sizeof(double));
-      p+= 2*item->path->num_points*sizeof(double);
-      if (item->brush.variable_width) {
-        g_memmove(p, item->widths, (item->path->num_points-1)*sizeof(double));
-        p+= (item->path->num_points-1)*sizeof(double);
-      }
-    }
-    if (item->type == ITEM_TEXT) {
-      g_memmove(p, &item->brush, sizeof(struct Brush)); p+= sizeof(struct Brush);
-      g_memmove(p, &item->bbox.left, sizeof(double)); p+= sizeof(double);
-      g_memmove(p, &item->bbox.top, sizeof(double)); p+= sizeof(double);
-      val = strlen(item->text);
-      g_memmove(p, &val, sizeof(int)); p+= sizeof(int);
-      g_memmove(p, item->text, val+1); p+= val+1;
-      val = strlen(item->font_name);
-      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);
-    }
-  }
-  
-  target.target = "_XOURNAL";
-  target.flags = 0;
-  target.info = 0;
-  
-  gtk_clipboard_set_with_data(gtk_clipboard_get(GDK_SELECTION_CLIPBOARD), 
-       &target, 1,
-       callback_clipboard_get, callback_clipboard_clear, buf);
-}
-
-
-void clipboard_paste(void)
-{
-  GtkSelectionData *sel_data;
-  unsigned char *p;
-  int nitems, npts, i, len;
-  struct Item *item;
-  double hoffset, voffset, cx, cy;
-  double *pf;
-  int sx, sy, wx, wy;
-  
-  if (ui.cur_layer == NULL) return;
-  
-  ui.cur_item_type = ITEM_PASTE;
-  sel_data = gtk_clipboard_wait_for_contents(
-      gtk_clipboard_get(GDK_SELECTION_CLIPBOARD),
-      gdk_atom_intern("_XOURNAL", FALSE));
-  ui.cur_item_type = ITEM_NONE;
-  if (sel_data == NULL) return; // paste failed
-  
-  reset_selection();
-  
-  ui.selection = g_new(struct Selection, 1);
-  p = sel_data->data + sizeof(int);
-  g_memmove(&nitems, p, sizeof(int)); p+= sizeof(int);
-  ui.selection->type = ITEM_SELECTRECT;
-  ui.selection->layer = ui.cur_layer;
-  g_memmove(&ui.selection->bbox, p, sizeof(struct BBox)); p+= sizeof(struct BBox);
-  ui.selection->items = NULL;
-  
-  // find by how much we translate the pasted selection
-  gnome_canvas_get_scroll_offsets(canvas, &sx, &sy);
-  gdk_window_get_geometry(GTK_WIDGET(canvas)->window, NULL, NULL, &wx, &wy, NULL);
-  gnome_canvas_window_to_world(canvas, sx + wx/2, sy + wy/2, &cx, &cy);
-  cx -= ui.cur_page->hoffset;
-  cy -= ui.cur_page->voffset;
-  if (cx + (ui.selection->bbox.right-ui.selection->bbox.left)/2 > ui.cur_page->width)
-    cx = ui.cur_page->width - (ui.selection->bbox.right-ui.selection->bbox.left)/2;
-  if (cx - (ui.selection->bbox.right-ui.selection->bbox.left)/2 < 0)
-    cx = (ui.selection->bbox.right-ui.selection->bbox.left)/2;
-  if (cy + (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 > ui.cur_page->height)
-    cy = ui.cur_page->height - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
-  if (cy - (ui.selection->bbox.bottom-ui.selection->bbox.top)/2 < 0)
-    cy = (ui.selection->bbox.bottom-ui.selection->bbox.top)/2;
-  hoffset = cx - (ui.selection->bbox.right+ui.selection->bbox.left)/2;
-  voffset = cy - (ui.selection->bbox.top+ui.selection->bbox.bottom)/2;
-  ui.selection->bbox.left += hoffset;
-  ui.selection->bbox.right += hoffset;
-  ui.selection->bbox.top += voffset;
-  ui.selection->bbox.bottom += voffset;
-
-  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);
-
-  while (nitems-- > 0) {
-    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++;
-    g_memmove(&item->type, p, sizeof(int)); p+= sizeof(int);
-    if (item->type == ITEM_STROKE) {
-      g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
-      g_memmove(&npts, p, sizeof(int)); p+= sizeof(int);
-      item->path = gnome_canvas_points_new(npts);
-      pf = (double *)p;
-      for (i=0; i<npts; i++) {
-        item->path->coords[2*i] = pf[2*i] + hoffset;
-        item->path->coords[2*i+1] = pf[2*i+1] + voffset;
-      }
-      p+= 2*item->path->num_points*sizeof(double);
-      if (item->brush.variable_width) {
-        item->widths = g_memdup(p, (item->path->num_points-1)*sizeof(double));
-        p+= (item->path->num_points-1)*sizeof(double);
-      }
-      else item->widths = NULL;
-      update_item_bbox(item);
-      make_canvas_item_one(ui.cur_layer->group, item);
-    }
-    if (item->type == ITEM_TEXT) {
-      g_memmove(&item->brush, p, sizeof(struct Brush)); p+= sizeof(struct Brush);
-      g_memmove(&item->bbox.left, p, sizeof(double)); p+= sizeof(double);
-      g_memmove(&item->bbox.top, p, sizeof(double)); p+= sizeof(double);
-      item->bbox.left += hoffset;
-      item->bbox.top += voffset;
-      g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
-      item->text = g_malloc(len+1);
-      g_memmove(item->text, p, len+1); p+= len+1;
-      g_memmove(&len, p, sizeof(int)); p+= sizeof(int);
-      item->font_name = g_malloc(len+1);
-      g_memmove(item->font_name, p, len+1); p+= len+1;
-      g_memmove(&item->font_size, p, sizeof(double)); p+= sizeof(double);
-      make_canvas_item_one(ui.cur_layer->group, item);
-    }
-  }
-
-  prepare_new_undo();
-  undo->type = ITEM_PASTE;
-  undo->layer = ui.cur_layer;
-  undo->itemlist = g_list_copy(ui.selection->items);  
-  
-  gtk_selection_data_free(sel_data);
-  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!
-}
-
 // modify the color or thickness of pen strokes in a selection
 
 void recolor_selection(int color_no, guint color_rgba)
 // modify the color or thickness of pen strokes in a selection
 
 void recolor_selection(int color_no, guint color_rgba)
index 4ee4e40d9fa99984c69351c22b39a7230f69abf4..8d69bd68efed31358f82d5a03894aefcec003b77 100644 (file)
@@ -38,8 +38,6 @@ void continue_resizesel(GdkEvent *event);
 void finalize_resizesel(void);
 
 void selection_delete(void);
 void finalize_resizesel(void);
 
 void selection_delete(void);
-void selection_to_clip(void);
-void clipboard_paste(void);
 
 void recolor_selection(int color_no, guint color_rgba);
 void rethicken_selection(int val);
 
 void recolor_selection(int color_no, guint color_rgba);
 void rethicken_selection(int val);
index 2cfc18ecd46dac68ac5b1581e504732e82a6d16c..029500ecba345d808c4ce4901227b185294dc32e 100644 (file)
@@ -787,6 +787,81 @@ int pdf_draw_bitmap_background(struct Page *pg, GString *str,
   return xref->last;
 }
 
   return xref->last;
 }
 
+gboolean pdf_draw_image(PdfImage *image, struct XrefTable *xref, GString *pdfbuf)
+{
+  char *buf, *p1, *p2;
+  int height, width, stride, x, y, chan;
+  GString *zpix;
+
+  if (gdk_pixbuf_get_bits_per_sample(image->pixbuf) != 8 ||
+      gdk_pixbuf_get_colorspace(image->pixbuf) != GDK_COLORSPACE_RGB) {
+    return FALSE;
+  }
+
+  width = gdk_pixbuf_get_width(image->pixbuf);
+  height = gdk_pixbuf_get_height(image->pixbuf);
+  stride = gdk_pixbuf_get_rowstride(image->pixbuf);
+  chan = gdk_pixbuf_get_n_channels(image->pixbuf);
+  if (!((chan==3 && !image->has_alpha) || (chan==4 && image->has_alpha))) {
+    return FALSE;
+  }
+
+  p2 = buf = (char *)g_malloc(3*width*height);
+  for (y=0; y<height; y++) {
+    p1 = (char *)gdk_pixbuf_get_pixels(image->pixbuf)+stride*y;
+    for (x=0; x<width; x++) {
+      *(p2++)=*(p1++); *(p2++)=*(p1++); *(p2++)=*(p1++);
+      if (chan==4) p1++;
+    }
+  }
+  zpix = do_deflate(buf, 3*width*height);
+  g_free(buf);
+
+  xref->data[image->n_obj] = pdfbuf->len;
+  g_string_append_printf(pdfbuf, 
+    "%d 0 obj\n<< /Length %d /Filter /FlateDecode /Type /Xobject "
+    "/Subtype /Image /Width %d /Height %d /ColorSpace /DeviceRGB "
+    "/BitsPerComponent 8 ",
+    image->n_obj, zpix->len, width, height);
+  if (image->has_alpha) {
+    g_string_append_printf(pdfbuf, 
+      "/SMask %d 0 R ",
+      image->n_obj_smask);
+  }
+  g_string_append_printf(pdfbuf, " >> stream\n");
+
+  g_string_append_len(pdfbuf, zpix->str, zpix->len);
+  g_string_free(zpix, TRUE);
+  g_string_append(pdfbuf, "endstream\nendobj\n");
+
+  if (image->has_alpha) {
+    p2 = buf = (char *)g_malloc(width*height);
+    for (y=0; y<height; y++) {
+      p1 = (char *)gdk_pixbuf_get_pixels(image->pixbuf)+stride*y;
+      for (x=0; x<width; x++) {
+        p1+=3;                  /* skip the RGB */
+        *(p2++)=*(p1++);        /* just copy the alpha */
+      }
+    }
+    zpix = do_deflate(buf, width*height);
+    g_free(buf);
+    
+    xref->data[image->n_obj_smask] = pdfbuf->len;
+    g_string_append_printf(pdfbuf, 
+      "%d 0 obj\n<< /Length %d /Filter /FlateDecode /Type /Xobject "
+      "/Subtype /Image /Width %d /Height %d /ColorSpace /DeviceGray "
+      "/BitsPerComponent 8 >> stream\n",
+      image->n_obj_smask, zpix->len, width, height);
+
+    g_string_append_len(pdfbuf, zpix->str, zpix->len);
+    g_string_free(zpix, TRUE);
+    g_string_append(pdfbuf, "endstream\nendobj\n");
+  }
+
+  return TRUE;
+}
+
+
 // manipulate Pdf fonts
 
 struct PdfFont *new_pdffont(struct XrefTable *xref, GList **fonts,
 // manipulate Pdf fonts
 
 struct PdfFont *new_pdffont(struct XrefTable *xref, GList **fonts,
@@ -1023,10 +1098,31 @@ void embed_pdffont(GString *pdfbuf, struct XrefTable *xref, struct PdfFont *font
   g_string_append(pdfbuf, ">> endobj\n");
 }
 
   g_string_append(pdfbuf, ">> endobj\n");
 }
 
+// Pdf images
+
+struct PdfImage *new_pdfimage(struct XrefTable *xref, GList **images, GdkPixbuf *pixbuf)
+{
+  GList *list;
+  struct PdfImage *image;
+  
+  image = g_malloc(sizeof(struct PdfImage));
+  *images = g_list_append(*images, image);
+  image->n_obj = xref->last+1;
+  make_xref(xref, xref->last+1, 0); // will give it a value later
+  image->has_alpha = gdk_pixbuf_get_has_alpha(pixbuf);
+  if (image->has_alpha) {
+    image->n_obj_smask = xref->last+1;
+    make_xref(xref, xref->last+1, 0); // will give it a value later
+  }
+  image->pixbuf = pixbuf;
+
+  return image;
+}
+
 // draw a page's graphics
 
 void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter, 
 // draw a page's graphics
 
 void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter, 
-                  struct XrefTable *xref, GList **pdffonts)
+                   struct XrefTable *xref, GList **pdffonts, GList **pdfimages)
 {
   GList *layerlist, *itemlist, *tmplist;
   struct Layer *l;
 {
   GList *layerlist, *itemlist, *tmplist;
   struct Layer *l;
@@ -1051,6 +1147,7 @@ void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter,
   int font_id;
   FT_Face ftface;
   struct PdfFont *cur_font;
   int font_id;
   FT_Face ftface;
   struct PdfFont *cur_font;
+  struct PdfImage *cur_image;
   gboolean in_string;
   
   old_rgba = old_text_rgba = 0x12345678;    // not any values we use, so we'll reset them
   gboolean in_string;
   
   old_rgba = old_text_rgba = 0x12345678;    // not any values we use, so we'll reset them
@@ -1059,6 +1156,10 @@ void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter,
     cur_font = (struct PdfFont *)tmplist->data;
     cur_font->used_in_this_page = FALSE;
   }
     cur_font = (struct PdfFont *)tmplist->data;
     cur_font->used_in_this_page = FALSE;
   }
+  for (tmplist = *pdfimages; tmplist!=NULL; tmplist = tmplist->next) {
+    cur_image = (struct PdfImage *)tmplist->data;
+    cur_image->used_in_this_page = FALSE;
+  }
 
   for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
     l = (struct Layer *)layerlist->data;
 
   for (layerlist = pg->layers; layerlist!=NULL; layerlist = layerlist->next) {
     l = (struct Layer *)layerlist->data;
@@ -1178,6 +1279,14 @@ void pdf_draw_page(struct Page *pg, GString *str, gboolean *use_hiliter,
         pango_layout_iter_free(iter);
         g_object_unref(layout);
       }
         pango_layout_iter_free(iter);
         g_object_unref(layout);
       }
+      else if  (item->type == ITEM_IMAGE) {
+        cur_image = new_pdfimage(xref, pdfimages, item->image);
+       cur_image->used_in_this_page = TRUE;
+        g_string_append_printf(str, "\nq 1 0 0 1 %.2f %.2f cm %.2f 0 0 %.2f 0 %.2f cm /Im%d Do Q ",
+           item->bbox.left, item->bbox.top, // translation
+           item->bbox.right-item->bbox.left, item->bbox.top-item->bbox.bottom, item->bbox.bottom-item->bbox.top, // scaling
+           cur_image->n_obj);
+      }
     }
   }
 }
     }
   }
 }
@@ -1204,8 +1313,9 @@ gboolean print_to_pdf(char *filename)
   gboolean use_hiliter;
   struct PdfInfo pdfinfo;
   struct PdfObj *obj;
   gboolean use_hiliter;
   struct PdfInfo pdfinfo;
   struct PdfObj *obj;
-  GList *pdffonts, *list;
+  GList *pdffonts, *pdfimages, *list;
   struct PdfFont *font;
   struct PdfFont *font;
+  struct PdfImage *image;
   char *tmpbuf;
   
   f = fopen(filename, "wb");
   char *tmpbuf;
   
   f = fopen(filename, "wb");
@@ -1215,6 +1325,7 @@ gboolean print_to_pdf(char *filename)
   xref.data = NULL;
   uses_pdf = FALSE;
   pdffonts = NULL;
   xref.data = NULL;
   uses_pdf = FALSE;
   pdffonts = NULL;
+  pdfimages = NULL;
   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
     pg = (struct Page *)pglist->data;
     if (pg->bg->type == BG_PDF) uses_pdf = TRUE;
   for (pglist = journal.pages; pglist!=NULL; pglist = pglist->next) {
     pg = (struct Page *)pglist->data;
     if (pg->bg->type == BG_PDF) uses_pdf = TRUE;
@@ -1284,7 +1395,7 @@ gboolean print_to_pdf(char *filename)
       n_obj_bgpix = pdf_draw_bitmap_background(pg, pgstrm, &xref, pdfbuf);
     // draw the page contents
     use_hiliter = FALSE;
       n_obj_bgpix = pdf_draw_bitmap_background(pg, pgstrm, &xref, pdfbuf);
     // draw the page contents
     use_hiliter = FALSE;
-    pdf_draw_page(pg, pgstrm, &use_hiliter, &xref, &pdffonts);
+    pdf_draw_page(pg, pgstrm, &use_hiliter, &xref, &pdffonts, &pdfimages);
     g_string_append_printf(pgstrm, "Q\n");
     
     // deflate pgstrm and write it
     g_string_append_printf(pgstrm, "Q\n");
     
     // deflate pgstrm and write it
@@ -1340,7 +1451,7 @@ gboolean print_to_pdf(char *filename)
     }
     add_dict_subentry(pdfbuf, &xref,
         obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/PDF"));
     }
     add_dict_subentry(pdfbuf, &xref,
         obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/PDF"));
-    if (n_obj_bgpix>0)
+    if (n_obj_bgpix>0 || pdfimages!=NULL)
       add_dict_subentry(pdfbuf, &xref,
         obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/ImageC"));
     if (use_hiliter)
       add_dict_subentry(pdfbuf, &xref,
         obj, "/ProcSet", PDFTYPE_ARRAY, NULL, mk_pdfname("/ImageC"));
     if (use_hiliter)
@@ -1360,12 +1471,21 @@ gboolean print_to_pdf(char *filename)
         g_free(tmpbuf);
       }
     }
         g_free(tmpbuf);
       }
     }
+    for (list=pdfimages; list!=NULL; list = list->next) {
+      image = (struct PdfImage *)list->data;
+      if (image->used_in_this_page) {
+        tmpbuf = g_strdup_printf("/Im%d", image->n_obj);
+        add_dict_subentry(pdfbuf, &xref,
+          obj, "/XObject", PDFTYPE_DICT, tmpbuf, mk_pdfref(image->n_obj));
+        g_free(tmpbuf);
+      }
+    }
     show_pdfobj(obj, pdfbuf);
     free_pdfobj(obj);
     g_string_append(pdfbuf, " >> endobj\n");
   }
   
     show_pdfobj(obj, pdfbuf);
     free_pdfobj(obj);
     g_string_append(pdfbuf, " >> endobj\n");
   }
   
-  // after the pages, we insert fonts
+  // after the pages, we insert fonts and images
   for (list = pdffonts; list!=NULL; list = list->next) {
     font = (struct PdfFont *)list->data;
     embed_pdffont(pdfbuf, &xref, font);
   for (list = pdffonts; list!=NULL; list = list->next) {
     font = (struct PdfFont *)list->data;
     embed_pdffont(pdfbuf, &xref, font);
@@ -1374,6 +1494,14 @@ gboolean print_to_pdf(char *filename)
     g_free(font);
   }
   g_list_free(pdffonts);
     g_free(font);
   }
   g_list_free(pdffonts);
+  for (list = pdfimages; list!=NULL; list = list->next) {
+    image = (struct PdfImage *)list->data;
+    if (!pdf_draw_image(image, &xref, pdfbuf)) {
+      return FALSE;
+    }
+    g_free(image);
+  }
+  g_list_free(pdfimages);
   
   // PDF trailer
   startxref = pdfbuf->len;
   
   // PDF trailer
   startxref = pdfbuf->len;
@@ -1565,6 +1693,16 @@ void print_job_render_page(GtkPrintOperation *print, GtkPrintContext *context, g
         pango_cairo_show_layout(cr, layout);
         g_object_unref(layout);
       }
         pango_cairo_show_layout(cr, layout);
         g_object_unref(layout);
       }
+      if (item->type == ITEM_IMAGE) {
+        double scalex = (item->bbox.right-item->bbox.left)/gdk_pixbuf_get_width(item->image);
+        double scaley = (item->bbox.bottom-item->bbox.top)/gdk_pixbuf_get_height(item->image);
+        cairo_scale(cr, scalex, scaley);
+        gdk_cairo_set_source_pixbuf(cr,item->image, item->bbox.left/scalex, item->bbox.top/scaley);
+        cairo_scale(cr, 1/scalex, 1/scaley);
+        cairo_paint(cr);
+        old_rgba = predef_colors_rgba[COLOR_BLACK];
+        cairo_set_source_rgb(cr, 0, 0, 0);
+      }
     }
   }
 }
     }
   }
 }
index 95a96f7e826b36d62e72ceb83a4cfad5d9dc9feb..498be84f0053b5d0a45404ec2effcf75af8fc5d2 100644 (file)
@@ -60,6 +60,15 @@ typedef struct PdfFont {
   int flags;
 } PdfFont;
 
   int flags;
 } PdfFont;
 
+typedef struct PdfImage {
+  int n_obj;
+  gboolean has_alpha;
+  int n_obj_smask;              /* only if has_alpha */
+  GdkPixbuf *pixbuf;
+  gboolean used_in_this_page;
+} PdfImage;
+
+
 #define PDFTYPE_CST 0    // intval: true=1, false=0, null=-1
 #define PDFTYPE_INT 1    // intval
 #define PDFTYPE_REAL 2   // realval
 #define PDFTYPE_CST 0    // intval: true=1, false=0, null=-1
 #define PDFTYPE_INT 1    // intval
 #define PDFTYPE_REAL 2   // realval
index 605c8232e133af4c992857e0303b7a3751faae74..9f801cc7154fe2a0ce7ebf49d8ebe4c320a6d606 100644 (file)
@@ -143,8 +143,9 @@ extern guint predef_bgcolors_rgba[COLOR_MAX];
 #define TOOL_SELECTRECT   5
 #define TOOL_VERTSPACE    6
 #define TOOL_HAND         7
 #define TOOL_SELECTRECT   5
 #define TOOL_VERTSPACE    6
 #define TOOL_HAND         7
+#define TOOL_IMAGE        8
 #define NUM_STROKE_TOOLS  3
 #define NUM_STROKE_TOOLS  3
-#define NUM_TOOLS         8
+#define NUM_TOOLS         9
 #define NUM_BUTTONS       3
 
 #define TOOLOPT_ERASER_STANDARD     0
 #define NUM_BUTTONS       3
 
 #define TOOLOPT_ERASER_STANDARD     0
@@ -173,6 +174,10 @@ typedef struct Item {
   gchar *font_name;
   gdouble font_size;
   GtkWidget *widget; // the widget while text is being edited (ITEM_TEMP_TEXT)
   gchar *font_name;
   gdouble font_size;
   GtkWidget *widget; // the widget while text is being edited (ITEM_TEMP_TEXT)
+  // the following fields for ITEM_IMAGE:
+  GdkPixbuf *image;  // the image
+  gchar *image_png;  // PNG of original image, for save and clipboard
+  gsize image_png_len;
 } Item;
 
 // item type values for Item.type, UndoItem.type, ui.cur_item_type ...
 } Item;
 
 // item type values for Item.type, UndoItem.type, ui.cur_item_type ...
@@ -201,6 +206,7 @@ typedef struct Item {
 #define ITEM_TEXT_ATTRIB 21
 #define ITEM_RESIZESEL 22
 #define ITEM_RECOGNIZER 23
 #define ITEM_TEXT_ATTRIB 21
 #define ITEM_RESIZESEL 22
 #define ITEM_RECOGNIZER 23
+#define ITEM_IMAGE 24
 
 typedef struct Layer {
   GList *items; // the items on the layer, from bottom to top
 
 typedef struct Layer {
   GList *items; // the items on the layer, from bottom to top
@@ -275,6 +281,7 @@ typedef struct UIData {
   gboolean hand_scrollto_pending;
   char *filename;
   gchar *default_path; // default path for new notes
   gboolean hand_scrollto_pending;
   char *filename;
   gchar *default_path; // default path for new notes
+  gchar *default_image; // path for previous image
   gboolean view_continuous, fullscreen, maximize_at_start;
   gboolean in_update_page_stuff; // semaphore to avoid scrollbar retroaction
   struct Selection *selection;
   gboolean view_continuous, fullscreen, maximize_at_start;
   gboolean in_update_page_stuff; // semaphore to avoid scrollbar retroaction
   struct Selection *selection;
@@ -326,8 +333,8 @@ typedef struct UndoErasureData {
 
 typedef struct UndoItem {
   int type;
 
 typedef struct UndoItem {
   int type;
-  struct Item *item; // for ITEM_STROKE, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_TEXT_ATTRIB
-  struct Layer *layer; // for ITEM_STROKE, ITEM_ERASURE, ITEM_PASTE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_MOVESEL, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_RECOGNIZER
+  struct Item *item; // for ITEM_STROKE, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_TEXT_ATTRIB, ITEM_IMAGE
+  struct Layer *layer; // for ITEM_STROKE, ITEM_ERASURE, ITEM_PASTE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_MOVESEL, ITEM_TEXT, ITEM_TEXT_EDIT, ITEM_RECOGNIZER, ITEM_IMAGE
   struct Layer *layer2; // for ITEM_DELETE_LAYER with val=-1, ITEM_MOVESEL
   struct Page *page;  // for ITEM_NEW_BG_ONE/RESIZE, ITEM_NEW_PAGE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_DELETE_PAGE
   GList *erasurelist; // for ITEM_ERASURE, ITEM_RECOGNIZER
   struct Layer *layer2; // for ITEM_DELETE_LAYER with val=-1, ITEM_MOVESEL
   struct Page *page;  // for ITEM_NEW_BG_ONE/RESIZE, ITEM_NEW_PAGE, ITEM_NEW_LAYER, ITEM_DELETE_LAYER, ITEM_DELETE_PAGE
   GList *erasurelist; // for ITEM_ERASURE, ITEM_RECOGNIZER
index cebf20f927625e630d090a01e70f653ec9de732d..1dacfe5df01d4ee27499eb6c0a23f7b5e4aa8423 100644 (file)
          <child>
            <widget class="GtkMenuItem" id="menuJournal">
              <property name="visible">True</property>
          <child>
            <widget class="GtkMenuItem" id="menuJournal">
              <property name="visible">True</property>
-             <property name="label" translatable="yes">_Journal</property>
+             <property name="label" translatable="yes">_Page</property>
              <property name="use_underline">True</property>
 
              <child>
              <property name="use_underline">True</property>
 
              <child>
                    </widget>
                  </child>
 
                    </widget>
                  </child>
 
+                 <child>
+                   <widget class="GtkRadioMenuItem" id="toolsImage">
+                     <property name="visible">True</property>
+                     <property name="label" translatable="yes">_Image</property>
+                     <property name="use_underline">True</property>
+                     <property name="active">True</property>
+                     <property name="group">toolsPen</property>
+                     <signal name="toggled" handler="on_toolsImage_activate" last_modification_time="Wed, 27 Jun 2012 20:54:08 GMT"/>
+                     <accelerator key="I" modifiers="GDK_CONTROL_MASK | GDK_SHIFT_MASK" signal="activate"/>
+                   </widget>
+                 </child>
+
                  <child>
                    <widget class="GtkSeparatorMenuItem" id="separator15">
                      <property name="visible">True</property>
                  <child>
                    <widget class="GtkSeparatorMenuItem" id="separator15">
                      <property name="visible">True</property>
                            </widget>
                          </child>
 
                            </widget>
                          </child>
 
+                         <child>
+                           <widget class="GtkRadioMenuItem" id="button2Image">
+                             <property name="visible">True</property>
+                             <property name="label" translatable="yes">_Image</property>
+                             <property name="use_underline">True</property>
+                             <property name="active">True</property>
+                             <property name="group">button2Pen</property>
+                             <signal name="activate" handler="on_button2Image_activate" last_modification_time="Wed, 27 Jun 2012 14:33:10 GMT"/>
+                           </widget>
+                         </child>
+
                          <child>
                            <widget class="GtkRadioMenuItem" id="button2SelectRegion">
                              <property name="visible">True</property>
                          <child>
                            <widget class="GtkRadioMenuItem" id="button2SelectRegion">
                              <property name="visible">True</property>
                            </widget>
                          </child>
 
                            </widget>
                          </child>
 
+                         <child>
+                           <widget class="GtkRadioMenuItem" id="button3Image">
+                             <property name="visible">True</property>
+                             <property name="label" translatable="yes">_Image</property>
+                             <property name="use_underline">True</property>
+                             <property name="active">True</property>
+                             <property name="group">button3Pen</property>
+                             <signal name="activate" handler="on_button3Image_activate" last_modification_time="Wed, 27 Jun 2012 14:33:10 GMT"/>
+                           </widget>
+                         </child>
+
                          <child>
                            <widget class="GtkRadioMenuItem" id="button3SelectRegion">
                              <property name="visible">True</property>
                          <child>
                            <widget class="GtkRadioMenuItem" id="button3SelectRegion">
                              <property name="visible">True</property>
            </packing>
          </child>
 
            </packing>
          </child>
 
+         <child>
+           <widget class="GtkRadioToolButton" id="buttonImage">
+             <property name="visible">True</property>
+             <property name="tooltip" translatable="yes">Image</property>
+             <property name="label" translatable="yes">Image</property>
+             <property name="use_underline">True</property>
+             <property name="stock_id">gtk-orientation-portrait</property>
+             <property name="visible_horizontal">True</property>
+             <property name="visible_vertical">True</property>
+             <property name="is_important">False</property>
+             <property name="active">False</property>
+             <property name="group">buttonPen</property>
+             <signal name="toggled" handler="on_toolsImage_activate" last_modification_time="Wed, 27 Jun 2012 20:49:10 GMT"/>
+           </widget>
+           <packing>
+             <property name="expand">False</property>
+             <property name="homogeneous">True</property>
+           </packing>
+         </child>
+
          <child>
            <widget class="GtkToggleToolButton" id="buttonReco">
              <property name="visible">True</property>
          <child>
            <widget class="GtkToggleToolButton" id="buttonReco">
              <property name="visible">True</property>