From 9f09269d8918dfa930543f4a15de4e7276719e5e Mon Sep 17 00:00:00 2001 From: auroux <auroux> Date: Thu, 5 Jul 2012 01:38:24 +0000 Subject: [PATCH] Lasso selection tool; prepare for release 0.4.7 --- AUTHORS | 6 +- ChangeLog | 3 +- NEWS | 4 +- README | 4 +- configure.in | 2 +- html-doc/manual.html | 45 +++++++++++-- src/xo-callbacks.c | 33 ++++++++- src/xo-file.c | 8 +-- src/xo-misc.c | 8 +-- src/xo-selection.c | 157 ++++++++++++++++++++++++++++++++++++++++++- src/xo-selection.h | 3 + src/xournal.h | 3 +- 12 files changed, 245 insertions(+), 31 deletions(-) diff --git a/AUTHORS b/AUTHORS index 7950cc0..ca2d698 100644 --- a/AUTHORS +++ b/AUTHORS @@ -5,9 +5,9 @@ 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, 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, -Jean-Baptiste Rouquier, Victor Saase, Marco Souza, Mike Ter Louw, +Lukasz Kaiser, Ian Woo Kim, Timo Kluck, David Kolibac, Danny Kukawka, +Stefan Lembach, Bob McElrath, Andy Neitzke, David Planella, Marco Poletti, +Alex Ray, 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 diff --git a/ChangeLog b/ChangeLog index 43c50f0..9b72ce1 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,7 +1,8 @@ -This version: +Version 0.4.7 (July 4, 2012): - insert image tool (based on patches by Victor Saase and Simon Guest) - renamed "Journal" menu to "Page" - paste images and text directly from and to other applications + - lasso tool (based on patch by Ian Woo Kim) Version 0.4.6 (May 22, 2012): - win32 portability code (contributed by Dirk Gerrits) diff --git a/NEWS b/NEWS index ad92faf..37cbe88 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,4 @@ -Version 0.4.6 (May 22, 2012) - -This release catches up with changes accumulated in the cvs repository. +Version 0.4.7 (July 4, 2012) Installation: see INSTALL User's manual: see html-doc/manual.html diff --git a/README b/README index ad92faf..37cbe88 100644 --- a/README +++ b/README @@ -1,6 +1,4 @@ -Version 0.4.6 (May 22, 2012) - -This release catches up with changes accumulated in the cvs repository. +Version 0.4.7 (July 4, 2012) Installation: see INSTALL User's manual: see html-doc/manual.html diff --git a/configure.in b/configure.in index 99d500b..1af1b0d 100644 --- a/configure.in +++ b/configure.in @@ -1,7 +1,7 @@ dnl Process this file with autoconf to produce a configure script. AC_INIT(configure.in) -AM_INIT_AUTOMAKE(xournal, 0.4.6+image) +AM_INIT_AUTOMAKE(xournal, 0.4.7) AM_CONFIG_HEADER(config.h) AM_MAINTAINER_MODE diff --git a/html-doc/manual.html b/html-doc/manual.html index 8a257e6..743d81e 100644 --- a/html-doc/manual.html +++ b/html-doc/manual.html @@ -24,7 +24,7 @@ Xournal User's Manual </h2> <p style="font-size: 0.95em; text-align: center; color: rgb(0,0,0)"> - Version 0.4.6 + Version 0.4.7 </p> <hr /> <p> @@ -148,6 +148,15 @@ the Tools menu): that the typesetting of the text may be slightly different in the printout. </p> +<h3 class="subsub"> The image tool</h3> +<p> + To insert a new image (from a file on disk), click at the location + where the upper-left corner is to be located. A file selection dialog + box pops up. Alternatively, images can be pasted directly from the + clipboard (without having to select the image tool). In both cases, + the newly inserted image is selected, and can be easily moved or resized + as with any selection. +</p> <h3 class="subsub"><img src="pixmaps/ruler.png"> The ruler</h3> <p> The ruler is not a tool by itself, but rather a special operating mode @@ -217,6 +226,13 @@ the Tools menu): or to a different journal) using the copy-paste toolbar buttons or the corresponding entries of the Edit menu. </p> +<h3 class="subsub"><img src="pixmaps/lasso.png"> Lasso selection</h3> +<p> + This tool lets you select an irregular shaped region of the current layer. + All the items which are entirely contained within the given region + are selected. As with the rectangle selection tool, the selection can be moved, + resized, copied and pasted. +</p> <h3 class="subsub"><img src="pixmaps/stretch.png"> Vertical space</h3> <p> This tool lets you insert or remove vertical space within the page: @@ -702,10 +718,11 @@ Xournal is written by Denis Auroux The source code includes contributions by the following people: Alvaro, Kit Barnes, Eduardo de Barros Lima, Mathieu Bouchard, Ole Jørgen Brønner, Robert Buchholz, Vincenzo Ciancia, Luca de Cicco, -Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits, -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. +Michele Codutti, Robert Gerlach, Daniel German, Dirk Gerrits, Simon Guest, +Lukasz Kaiser, Ian Woo Kim, Timo Kluck, David Kolibac, Danny Kukawka, +Stefan Lembach, Bob McElrath, Andy Neitzke, David Planella, Marco Poletti, +Alex Ray, Jean-Baptiste Rouquier, Victor Saase, Marco Souza, Mike Ter Louw, +Uwe Winter, Lu Zhihe. </p> <p style="font-size:0.9em">(Let me know if you are missing from this list or if your name is mis-spelled)</p> @@ -734,6 +751,15 @@ Bug reports and suggestions can also be submitted on Xournal's <a name="changelog"></a> <h2 class="subtitle">Version history</h2> <p> +Version 0.4.7 (July 4, 2012): +<ul> + <li>insert image tool (based on patches by Victor Saase and Simon Guest) +</li><li>renamed "Journal" menu to "Page" +</li><li>paste images and text directly from and to other applications +</li><li>lasso tool (based on patch by Ian Woo Kim) +</ul> +</p> +<p> Version 0.4.6 (May 22, 2012): <ul> <li>win32 portability code (contributed by Dirk Gerrits) @@ -1080,6 +1106,15 @@ The contents of the text are encoded in UTF-8, with the characters no extraneous whitespace should be inserted between the enclosing tags and the text itself). </p> +<p>Starting with version 0.4.7, layers can also contain image items. +The format of an image item is: +<pre><image left="..." top="..." right="..." bottom="...">... data ...</image> +</pre> +The <i>left</i>, <i>top</i>, <i>right</i> and <i>bottom</i> attributes +specify the bounding box to which the image is scaled, in page coordinates +(measured in points from the top-left corner). The data is in base64-encoded +PNG format (though any other base64-encoded format that can be loaded by +gdk-pixbuf is currently accepted). <hr /> <a name="installation"></a> <h2 class="subtitle">Installation issues</h2> diff --git a/src/xo-callbacks.c b/src/xo-callbacks.c index 8c6f098..50672f6 100644 --- a/src/xo-callbacks.c +++ b/src/xo-callbacks.c @@ -33,6 +33,7 @@ #include "xo-misc.h" #include "xo-file.h" #include "xo-paint.h" +#include "xo-selection.h" #include "xo-print.h" #include "xo-shapes.h" @@ -1807,7 +1808,25 @@ void on_toolsSelectRegion_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_SELECTREGION) return; + + ui.cur_mapping = 0; // don't use switch_mapping() (refreshes buttons too soon) + end_text(); + ui.toolno[ui.cur_mapping] = TOOL_SELECTREGION; + update_mapping_linkings(-1); + update_tool_buttons(); + update_tool_menu(); + update_color_menu(); + update_cursor(); } @@ -2520,6 +2539,9 @@ on_canvas_button_press_event (GtkWidget *widget, do_eraser((GdkEvent *)event, ui.cur_brush->thickness/2, ui.cur_brush->tool_options == TOOLOPT_ERASER_STROKES); } + else if (ui.toolno[mapping] == TOOL_SELECTREGION) { + start_selectregion((GdkEvent *)event); + } else if (ui.toolno[mapping] == TOOL_SELECTRECT) { start_selectrect((GdkEvent *)event); } @@ -2564,6 +2586,9 @@ on_canvas_button_release_event (GtkWidget *widget, else if (ui.cur_item_type == ITEM_ERASURE) { finalize_erasure(); } + else if (ui.cur_item_type == ITEM_SELECTREGION) { + finalize_selectregion(); + } else if (ui.cur_item_type == ITEM_SELECTRECT) { finalize_selectrect(); } @@ -2756,6 +2781,9 @@ on_canvas_motion_notify_event (GtkWidget *widget, else if (ui.cur_item_type == ITEM_ERASURE) { finalize_erasure(); } + else if (ui.cur_item_type == ITEM_SELECTREGION) { + finalize_selectregion(); + } else if (ui.cur_item_type == ITEM_SELECTRECT) { finalize_selectrect(); } @@ -2779,6 +2807,9 @@ on_canvas_motion_notify_event (GtkWidget *widget, do_eraser((GdkEvent *)event, ui.cur_brush->thickness/2, ui.cur_brush->tool_options == TOOLOPT_ERASER_STROKES); } + else if (ui.cur_item_type == ITEM_SELECTREGION) { + continue_selectregion((GdkEvent *)event); + } else if (ui.cur_item_type == ITEM_SELECTRECT) { get_pointer_coords((GdkEvent *)event, pt); ui.selection->bbox.right = pt[0]; diff --git a/src/xo-file.c b/src/xo-file.c index 129ba87..082d399 100644 --- a/src/xo-file.c +++ b/src/xo-file.c @@ -45,7 +45,7 @@ #include "xo-paint.h" #include "xo-image.h" -const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "", "selectrect", "vertspace", "hand", "image"}; +const char *tool_names[NUM_TOOLS] = {"pen", "eraser", "highlighter", "text", "selectregion", "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"}; @@ -1709,7 +1709,7 @@ void save_config_to_file(void) g_strdup_printf("%d", PDFTOPPM_PRINTING_DPI)); update_keyval("tools", "startup_tool", - _(" selected tool at startup (pen, eraser, highlighter, selectrect, vertspace, hand)"), + _(" selected tool at startup (pen, eraser, highlighter, selectregion, selectrect, vertspace, hand, image)"), g_strdup(tool_names[ui.startuptool])); update_keyval("tools", "pen_color", _(" default pen color"), @@ -1746,7 +1746,7 @@ void save_config_to_file(void) _(" default highlighter is in shape recognizer mode (true/false)"), g_strdup(ui.default_brushes[TOOL_HIGHLIGHTER].recognizer?"true":"false")); update_keyval("tools", "btn2_tool", - _(" button 2 tool (pen, eraser, highlighter, text, selectrect, vertspace, hand)"), + _(" button 2 tool (pen, eraser, highlighter, text, selectregion, selectrect, vertspace, hand, image)"), g_strdup(tool_names[ui.toolno[1]])); update_keyval("tools", "btn2_linked", _(" button 2 brush linked to primary brush (true/false) (overrides all other settings)"), @@ -1774,7 +1774,7 @@ void save_config_to_file(void) _(" button 2 eraser mode (eraser only)"), g_strdup_printf("%d", ui.brushes[1][TOOL_ERASER].tool_options)); update_keyval("tools", "btn3_tool", - _(" button 3 tool (pen, eraser, highlighter, text, selectrect, vertspace, hand)"), + _(" button 3 tool (pen, eraser, highlighter, text, selectregion, selectrect, vertspace, hand, image)"), g_strdup(tool_names[ui.toolno[2]])); update_keyval("tools", "btn3_linked", _(" button 3 brush linked to primary brush (true/false) (overrides all other settings)"), diff --git a/src/xo-misc.c b/src/xo-misc.c index bd65f1a..3d866cf 100644 --- a/src/xo-misc.c +++ b/src/xo-misc.c @@ -2082,8 +2082,8 @@ void allow_all_accels(void) "can-activate-accel", G_CALLBACK(can_accel), NULL); g_signal_connect((gpointer) GET_COMPONENT("toolsText"), "can-activate-accel", G_CALLBACK(can_accel), NULL); -/* g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"), - "can-activate-accel", G_CALLBACK(can_accel), NULL); */ + g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRegion"), + "can-activate-accel", G_CALLBACK(can_accel), NULL); g_signal_connect((gpointer) GET_COMPONENT("toolsSelectRectangle"), "can-activate-accel", G_CALLBACK(can_accel), NULL); g_signal_connect((gpointer) GET_COMPONENT("toolsVerticalSpace"), @@ -2145,10 +2145,6 @@ void hide_unimplemented(void) { gtk_widget_hide(GET_COMPONENT("filePrintOptions")); gtk_widget_hide(GET_COMPONENT("journalFlatten")); - gtk_widget_hide(GET_COMPONENT("toolsSelectRegion")); - gtk_widget_hide(GET_COMPONENT("buttonSelectRegion")); - gtk_widget_hide(GET_COMPONENT("button2SelectRegion")); - gtk_widget_hide(GET_COMPONENT("button3SelectRegion")); gtk_widget_hide(GET_COMPONENT("helpIndex")); /* config file only works with glib 2.6 and beyond */ diff --git a/src/xo-selection.c b/src/xo-selection.c index 9b35864..5816bb6 100644 --- a/src/xo-selection.c +++ b/src/xo-selection.c @@ -22,6 +22,8 @@ #include <gtk/gtk.h> #include <libgnomecanvas/libgnomecanvas.h> #include <libart_lgpl/art_vpath_dash.h> +#include <libart_lgpl/art_svp_point.h> +#include <libart_lgpl/art_svp_vpath.h> #include "xournal.h" #include "xo-callbacks.h" @@ -74,7 +76,6 @@ void finalize_selectrect(void) double x1, x2, y1, y2; GList *itemlist; struct Item *item; - ui.cur_item_type = ITEM_NONE; @@ -119,6 +120,156 @@ void finalize_selectrect(void) update_font_button(); } + +void start_selectregion(GdkEvent *event) +{ + double pt[2]; + reset_selection(); + + ui.cur_item_type = ITEM_SELECTREGION; + ui.selection = g_new(struct Selection, 1); + ui.selection->type = ITEM_SELECTREGION; + ui.selection->items = NULL; + ui.selection->layer = ui.cur_layer; + + get_pointer_coords(event, pt); + ui.selection->bbox.left = ui.selection->bbox.right = pt[0]; + ui.selection->bbox.top = ui.selection->bbox.bottom = pt[1]; + + realloc_cur_path(1); + ui.cur_path.num_points = 1; + ui.cur_path.coords[0] = ui.cur_path.coords[2] = pt[0]; + ui.cur_path.coords[1] = ui.cur_path.coords[3] = pt[1]; + + ui.selection->canvas_item = gnome_canvas_item_new(ui.cur_layer->group, + gnome_canvas_polygon_get_type(), "width-pixels", 1, + "outline-color-rgba", 0x000000ff, + "fill-color-rgba", 0x80808040, + NULL); + make_dashed(ui.selection->canvas_item); + update_cursor(); +} + +void continue_selectregion(GdkEvent *event) +{ + double *pt; + + realloc_cur_path(ui.cur_path.num_points+1); + pt = ui.cur_path.coords + 2*ui.cur_path.num_points; + get_pointer_coords(event, pt); + if (hypot(pt[0]-pt[-2], pt[1]-pt[-1]) < PIXEL_MOTION_THRESHOLD/ui.zoom) + return; // not a meaningful motion + ui.cur_path.num_points++; + if (ui.cur_path.num_points>2) + gnome_canvas_item_set(ui.selection->canvas_item, + "points", &ui.cur_path, NULL); +} + +/* check whether a point, resp. an item, is inside a lasso selection */ + +gboolean hittest_point(ArtSVP *lassosvp, double x, double y) +{ + return art_svp_point_wind(lassosvp, x, y)%2; +} + +gboolean hittest_item(ArtSVP *lassosvp, struct Item *item) +{ + int i; + + if (item->type == ITEM_STROKE) { + for (i=0; i<item->path->num_points; i++) + if (!hittest_point(lassosvp, item->path->coords[2*i], item->path->coords[2*i+1])) + return FALSE; + return TRUE; + } + else + return (hittest_point(lassosvp, item->bbox.left, item->bbox.top) && + hittest_point(lassosvp, item->bbox.right, item->bbox.top) && + hittest_point(lassosvp, item->bbox.left, item->bbox.bottom) && + hittest_point(lassosvp, item->bbox.right, item->bbox.bottom)); +} + +void finalize_selectregion(void) +{ + GList *itemlist; + struct Item *item; + ArtVpath *vpath; + ArtSVP *lassosvp; + int i, n; + double *pt; + + ui.cur_item_type = ITEM_NONE; + + // build SVP for the lasso path + n = ui.cur_path.num_points; + vpath = g_malloc((n+2)*sizeof(ArtVpath)); + for (i=0; i<n; i++) { + vpath[i].x = ui.cur_path.coords[2*i]; + vpath[i].y = ui.cur_path.coords[2*i+1]; + } + vpath[n].x = vpath[0].x; vpath[n].y = vpath[0].y; + vpath[0].code = ART_MOVETO; + for (i=1; i<=n; i++) vpath[i].code = ART_LINETO; + vpath[n+1].code = ART_END; + lassosvp = art_svp_from_vpath(vpath); + g_free(vpath); + + // see which items we selected + for (itemlist = ui.selection->layer->items; itemlist!=NULL; itemlist = itemlist->next) { + item = (struct Item *)itemlist->data; + if (hittest_item(lassosvp, item)) { + // update the selection bbox + if (ui.selection->items==NULL || ui.selection->bbox.left>item->bbox.left) + ui.selection->bbox.left = item->bbox.left; + if (ui.selection->items==NULL || ui.selection->bbox.right<item->bbox.right) + ui.selection->bbox.right = item->bbox.right; + if (ui.selection->items==NULL || ui.selection->bbox.top>item->bbox.top) + ui.selection->bbox.top = item->bbox.top; + if (ui.selection->items==NULL || ui.selection->bbox.bottom<item->bbox.bottom) + ui.selection->bbox.bottom = item->bbox.bottom; + // add the item + ui.selection->items = g_list_append(ui.selection->items, item); + } + } + art_svp_free(lassosvp); + + if (ui.selection->items == NULL) { + // if we clicked inside a text zone or image? + pt = ui.cur_path.coords; + item = click_is_in_text_or_image(ui.selection->layer, pt[0], pt[1]); + if (item!=NULL) { + for (i=0; i<n; i++, pt+=2) { + if (pt[0]<item->bbox.left || pt[0]>item->bbox.right || pt[1]<item->bbox.top || pt[1]>item->bbox.bottom) + { item = NULL; break; } + } + } + if (item!=NULL) { + ui.selection->items = g_list_append(ui.selection->items, item); + g_memmove(&(ui.selection->bbox), &(item->bbox), sizeof(struct BBox)); + } + } + + if (ui.selection->items == NULL) reset_selection(); + else { // make a selection rectangle instead of the lasso shape + gtk_object_destroy(GTK_OBJECT(ui.selection->canvas_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); + ui.selection->type = ITEM_SELECTRECT; + } + + update_cursor(); + update_copy_paste_enabled(); + update_font_button(); +} + + +/*** moving/resizing the selection ***/ + gboolean start_movesel(GdkEvent *event) { double pt[2]; @@ -127,7 +278,7 @@ gboolean start_movesel(GdkEvent *event) if (ui.cur_layer != ui.selection->layer) return FALSE; get_pointer_coords(event, pt); - if (ui.selection->type == ITEM_SELECTRECT) { + if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) { if (pt[0]<ui.selection->bbox.left || pt[0]>ui.selection->bbox.right || pt[1]<ui.selection->bbox.top || pt[1]>ui.selection->bbox.bottom) return FALSE; @@ -154,7 +305,7 @@ gboolean start_resizesel(GdkEvent *event) get_pointer_coords(event, pt); - if (ui.selection->type == ITEM_SELECTRECT) { + if (ui.selection->type == ITEM_SELECTRECT || ui.selection->type == ITEM_SELECTREGION) { resize_margin = RESIZE_MARGIN/ui.zoom; hmargin = (ui.selection->bbox.right-ui.selection->bbox.left)*0.3; if (hmargin>resize_margin) hmargin = resize_margin; diff --git a/src/xo-selection.h b/src/xo-selection.h index bf5bce4..df1006a 100644 --- a/src/xo-selection.h +++ b/src/xo-selection.h @@ -16,6 +16,9 @@ void start_selectrect(GdkEvent *event); void finalize_selectrect(void); +void start_selectregion(GdkEvent *event); +void finalize_selectregion(void); +void continue_selectregion(GdkEvent *event); gboolean start_movesel(GdkEvent *event); void start_vertspace(GdkEvent *event); diff --git a/src/xournal.h b/src/xournal.h index 9f801cc..9d9064e 100644 --- a/src/xournal.h +++ b/src/xournal.h @@ -207,6 +207,7 @@ typedef struct Item { #define ITEM_RESIZESEL 22 #define ITEM_RECOGNIZER 23 #define ITEM_IMAGE 24 +#define ITEM_SELECTREGION 25 typedef struct Layer { GList *items; // the items on the layer, from bottom to top @@ -230,7 +231,7 @@ typedef struct Journal { } Journal; typedef struct Selection { - int type; // ITEM_SELECTRECT, ITEM_MOVESEL_VERT + int type; // ITEM_SELECTRECT, ITEM_MOVESEL_VERT, ITEM_SELECTREGION BBox bbox; // the rectangle bbox of the selection struct Layer *layer; // the layer on which the selection lives double anchor_x, anchor_y, last_x, last_y; // for selection motion -- 2.39.5