21
20
#include "config.h"
24
#include "trace/potrace/inkscape-potrace.h"
25
#include <2geom/pathvector.h>
26
23
#include <gdk/gdkkeysyms.h>
29
#include <glibmm/i18n.h>
32
#include "context-fns.h"
28
#include "display/sp-canvas.h"
30
#include "sp-namedview.h"
31
#include "sp-object.h"
33
#include "selection.h"
34
#include "desktop-handles.h"
33
35
#include "desktop.h"
35
36
#include "desktop-style.h"
36
#include "display/cairo-utils.h"
37
#include "display/drawing-context.h"
38
#include "display/drawing-image.h"
39
#include "display/drawing-item.h"
40
#include "display/drawing.h"
41
#include "display/sp-canvas.h"
43
#include "document-undo.h"
44
#include "ui/tools/flood-tool.h"
45
#include "livarot/Path.h"
46
#include "livarot/Shape.h"
37
#include "message-stack.h"
48
38
#include "message-context.h"
49
#include "message-stack.h"
39
#include "pixmaps/cursor-paintbucket.xpm"
40
#include "flood-context.h"
41
#include "sp-metrics.h"
42
#include <glibmm/i18n.h>
43
#include "object-edit.h"
45
#include "xml/node-event-vector.h"
50
46
#include "preferences.h"
47
#include "context-fns.h"
51
48
#include "rubberband.h"
52
#include "selection.h"
53
#include "ui/shape-editor.h"
49
#include "shape-editor.h"
51
#include "display/nr-arena-item.h"
52
#include "display/nr-arena.h"
53
#include "display/nr-arena-image.h"
54
#include "display/canvas-arena.h"
55
#include "libnr/nr-pixops.h"
56
#include "libnr/nr-matrix-translate-ops.h"
57
#include "libnr/nr-scale-ops.h"
58
#include "libnr/nr-scale-translate-ops.h"
59
#include "libnr/nr-translate-matrix-ops.h"
60
#include "libnr/nr-translate-scale-ops.h"
61
#include "libnr/nr-matrix-ops.h"
62
#include <2geom/pathvector.h>
54
65
#include "sp-defs.h"
56
67
#include "splivarot.h"
57
#include "sp-namedview.h"
58
#include "sp-object.h"
68
#include "livarot/Path.h"
69
#include "livarot/Shape.h"
62
70
#include "svg/svg.h"
73
#include "trace/trace.h"
63
74
#include "trace/imagemap.h"
64
#include "trace/trace.h"
65
#include "xml/node-event-vector.h"
69
#include "pixmaps/cursor-paintbucket.xpm"
71
using Inkscape::DocumentUndo;
73
using Inkscape::Display::ExtractARGB32;
74
using Inkscape::Display::ExtractRGB32;
75
using Inkscape::Display::AssembleARGB32;
81
const std::string& FloodTool::getPrefsPath() {
82
return FloodTool::prefsPath;
85
const std::string FloodTool::prefsPath = "/tools/paintbucket";
87
// TODO: Replace by C++11 initialization
88
// Must match PaintBucketChannels enum
89
Glib::ustring ch_init[8] = {
99
const std::vector<Glib::ustring> FloodTool::channel_list( ch_init, ch_init+8 );
101
Glib::ustring gap_init[4] = {
102
C_("Flood autogap", "None"),
103
C_("Flood autogap", "Small"),
104
C_("Flood autogap", "Medium"),
105
C_("Flood autogap", "Large")
107
const std::vector<Glib::ustring> FloodTool::gap_list( gap_init, gap_init+4 );
109
FloodTool::FloodTool()
110
: ToolBase(cursor_paintbucket_xpm, 11, 30)
113
// TODO: Why does the flood tool use a hardcoded tolerance instead of a pref?
117
FloodTool::~FloodTool() {
118
this->sel_changed_connection.disconnect();
120
delete this->shape_editor;
121
this->shape_editor = NULL;
75
#include "trace/potrace/inkscape-potrace.h"
77
static void sp_flood_context_class_init(SPFloodContextClass *klass);
78
static void sp_flood_context_init(SPFloodContext *flood_context);
79
static void sp_flood_context_dispose(GObject *object);
81
static void sp_flood_context_setup(SPEventContext *ec);
83
static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event);
84
static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event);
86
static void sp_flood_finish(SPFloodContext *rc);
88
static SPEventContextClass *parent_class;
91
GtkType sp_flood_context_get_type()
93
static GType type = 0;
96
sizeof(SPFloodContextClass),
98
(GClassInitFunc) sp_flood_context_class_init,
100
sizeof(SPFloodContext),
102
(GInstanceInitFunc) sp_flood_context_init,
103
NULL, /* value_table */
105
type = g_type_register_static(SP_TYPE_EVENT_CONTEXT, "SPFloodContext", &info, (GTypeFlags) 0);
110
static void sp_flood_context_class_init(SPFloodContextClass *klass)
112
GObjectClass *object_class = (GObjectClass *) klass;
113
SPEventContextClass *event_context_class = (SPEventContextClass *) klass;
115
parent_class = (SPEventContextClass *) g_type_class_peek_parent(klass);
117
object_class->dispose = sp_flood_context_dispose;
119
event_context_class->setup = sp_flood_context_setup;
120
event_context_class->root_handler = sp_flood_context_root_handler;
121
event_context_class->item_handler = sp_flood_context_item_handler;
124
static void sp_flood_context_init(SPFloodContext *flood_context)
126
SPEventContext *event_context = SP_EVENT_CONTEXT(flood_context);
128
event_context->cursor_shape = cursor_paintbucket_xpm;
129
event_context->hot_x = 11;
130
event_context->hot_y = 30;
131
event_context->xp = 0;
132
event_context->yp = 0;
133
event_context->tolerance = 4;
134
event_context->within_tolerance = false;
135
event_context->item_to_select = NULL;
137
flood_context->item = NULL;
139
new (&flood_context->sel_changed_connection) sigc::connection();
142
static void sp_flood_context_dispose(GObject *object)
144
SPFloodContext *rc = SP_FLOOD_CONTEXT(object);
145
SPEventContext *ec = SP_EVENT_CONTEXT(object);
147
rc->sel_changed_connection.disconnect();
148
rc->sel_changed_connection.~connection();
150
delete ec->shape_editor;
151
ec->shape_editor = NULL;
123
153
/* fixme: This is necessary because we do not grab */
158
if (rc->_message_context) {
159
delete rc->_message_context;
162
G_OBJECT_CLASS(parent_class)->dispose(object);
130
* Callback that processes the "changed" signal on the selection;
131
* destroys old and creates new knotholder.
133
void FloodTool::selection_changed(Inkscape::Selection* selection) {
134
this->shape_editor->unset_item();
135
this->shape_editor->set_item(selection->singleItem());
166
\brief Callback that processes the "changed" signal on the selection;
167
destroys old and creates new knotholder
169
void sp_flood_context_selection_changed(Inkscape::Selection *selection, gpointer data)
171
SPFloodContext *rc = SP_FLOOD_CONTEXT(data);
172
SPEventContext *ec = SP_EVENT_CONTEXT(rc);
174
ec->shape_editor->unset_item(SH_KNOTHOLDER);
175
SPItem *item = selection->singleItem();
176
ec->shape_editor->set_item(item, SH_KNOTHOLDER);
138
void FloodTool::setup() {
141
this->shape_editor = new ShapeEditor(this->desktop);
143
SPItem *item = this->desktop->getSelection()->singleItem();
179
static void sp_flood_context_setup(SPEventContext *ec)
181
SPFloodContext *rc = SP_FLOOD_CONTEXT(ec);
183
if (((SPEventContextClass *) parent_class)->setup) {
184
((SPEventContextClass *) parent_class)->setup(ec);
187
ec->shape_editor = new ShapeEditor(ec->desktop);
189
SPItem *item = sp_desktop_selection(ec->desktop)->singleItem();
145
this->shape_editor->set_item(item);
191
ec->shape_editor->set_item(item, SH_KNOTHOLDER);
148
this->sel_changed_connection.disconnect();
149
this->sel_changed_connection = this->desktop->getSelection()->connectChanged(
150
sigc::mem_fun(this, &FloodTool::selection_changed)
194
rc->sel_changed_connection.disconnect();
195
rc->sel_changed_connection = sp_desktop_selection(ec->desktop)->connectChanged(
196
sigc::bind(sigc::ptr_fun(&sp_flood_context_selection_changed), (gpointer)rc)
199
rc->_message_context = new Inkscape::MessageContext((ec->desktop)->messageStack());
153
201
Inkscape::Preferences *prefs = Inkscape::Preferences::get();
155
202
if (prefs->getBool("/tools/paintbucket/selcue")) {
156
this->enableSelectionCue();
203
rc->enableSelectionCue();
161
// Changes from 0.48 -> 0.49 (Cairo)
162
// 0.49: Ignores alpha in background
163
// 0.48: RGBA, 0.49 ARGB
164
// 0.49: premultiplied alpha
165
inline static guint32 compose_onto(guint32 px, guint32 bg)
208
* \brief Merge a pixel with the background color.
209
* \param orig The pixel to merge with the background.
210
* \param bg The background color.
211
* \param base The pixel to merge the original and background into.
214
merge_pixel_with_background (unsigned char *orig, unsigned char *bg,
167
guint ap = 0, rp = 0, gp = 0, bp = 0;
168
guint rb = 0, gb = 0, bb = 0;
169
ExtractARGB32(px, ap, rp, gp, bp);
170
ExtractRGB32(bg, rb, gb, bb);
172
// guint ao = 255*255 - (255-ap)*(255-bp); ao = (ao + 127) / 255;
173
// guint ao = (255-ap)*ab + 255*ap; ao = (ao + 127) / 255;
174
guint ao = 255; // Cairo version doesn't allow background to have alpha != 1.
175
guint ro = (255-ap)*rb + 255*rp; ro = (ro + 127) / 255;
176
guint go = (255-ap)*gb + 255*gp; go = (go + 127) / 255;
177
guint bo = (255-ap)*bb + 255*bp; bo = (bo + 127) / 255;
179
guint pxout = AssembleARGB32(ao, ro, go, bo);
217
int precalc_bg_alpha = (255 * (255 - bg[3])) / 255;
219
for (int i = 0; i < 3; i++) {
220
base[i] = precalc_bg_alpha + (bg[i] * bg[3]) / 255;
221
base[i] = (base[i] * (255 - orig[3])) / 255 + (orig[i] * orig[3]) / 255;
184
* Get the pointer to a pixel in a pixel buffer.
185
* @param px The pixel buffer.
186
* @param x The X coordinate.
187
* @param y The Y coordinate.
188
* @param stride The rowstride of the pixel buffer.
226
* \brief Get the pointer to a pixel in a pixel buffer.
227
* \param px The pixel buffer.
228
* \param x The X coordinate.
229
* \param y The Y coordinate.
230
* \param width The width of the pixel buffer.
190
inline guint32 get_pixel(guchar *px, int x, int y, int stride) {
191
return *reinterpret_cast<guint32*>(px + y * stride + x * 4);
232
inline unsigned char * get_pixel(guchar *px, int x, int y, int width) {
233
return px + (x + y * width) * 4;
194
236
inline unsigned char * get_trace_pixel(guchar *trace_px, int x, int y, int width) {
199
* Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
200
* @param check The pixel in the pixel buffer to check.
201
* @param orig The original selected pixel to use as the fill target color.
202
* @param merged_orig_pixel The original pixel merged with the background.
203
* @param dtc The desktop background color.
204
* @param threshold The fill threshold.
205
* @param method The fill method to use as defined in PaintBucketChannels.
207
static bool compare_pixels(guint32 check, guint32 orig, guint32 merged_orig_pixel, guint32 dtc, int threshold, PaintBucketChannels method)
241
* \brief Generate the list of trace channel selection entries.
243
GList * flood_channels_dropdown_items_list() {
246
glist = g_list_append (glist, _("Visible Colors"));
247
glist = g_list_append (glist, _("Red"));
248
glist = g_list_append (glist, _("Green"));
249
glist = g_list_append (glist, _("Blue"));
250
glist = g_list_append (glist, _("Hue"));
251
glist = g_list_append (glist, _("Saturation"));
252
glist = g_list_append (glist, _("Lightness"));
253
glist = g_list_append (glist, _("Alpha"));
259
* \brief Generate the list of autogap selection entries.
261
GList * flood_autogap_dropdown_items_list() {
264
glist = g_list_append (glist, _("None"));
265
glist = g_list_append (glist, _("Small"));
266
glist = g_list_append (glist, _("Medium"));
267
glist = g_list_append (glist, _("Large"));
273
* \brief Compare a pixel in a pixel buffer with another pixel to determine if a point should be included in the fill operation.
274
* \param check The pixel in the pixel buffer to check.
275
* \param orig The original selected pixel to use as the fill target color.
276
* \param merged_orig_pixel The original pixel merged with the background.
277
* \param dtc The desktop background color.
278
* \param threshold The fill threshold.
279
* \param method The fill method to use as defined in PaintBucketChannels.
281
static bool compare_pixels(unsigned char *check, unsigned char *orig, unsigned char *merged_orig_pixel, unsigned char *dtc, int threshold, PaintBucketChannels method) {
210
float hsl_check[3] = {0,0,0}, hsl_orig[3] = {0,0,0};
212
guint32 ac = 0, rc = 0, gc = 0, bc = 0;
213
ExtractARGB32(check, ac, rc, gc, bc);
215
guint32 ao = 0, ro = 0, go = 0, bo = 0;
216
ExtractARGB32(orig, ao, ro, go, bo);
218
guint32 ad = 0, rd = 0, gd = 0, bd = 0;
219
ExtractARGB32(dtc, ad, rd, gd, bd);
221
guint32 amop = 0, rmop = 0, gmop = 0, bmop = 0;
222
ExtractARGB32(merged_orig_pixel, amop, rmop, gmop, bmop);
283
float hsl_check[3], hsl_orig[3];
224
285
if ((method == FLOOD_CHANNELS_H) ||
225
286
(method == FLOOD_CHANNELS_S) ||
226
287
(method == FLOOD_CHANNELS_L)) {
229
sp_color_rgb_to_hsl_floatv(hsl_check, rc / dac, gc / dac, bc / dac);
230
sp_color_rgb_to_hsl_floatv(hsl_orig, ro / dao, go / dao, bo / dao);
288
sp_color_rgb_to_hsl_floatv(hsl_check, check[0] / 255.0, check[1] / 255.0, check[2] / 255.0);
289
sp_color_rgb_to_hsl_floatv(hsl_orig, orig[0] / 255.0, orig[1] / 255.0, orig[2] / 255.0);
233
292
switch (method) {
234
293
case FLOOD_CHANNELS_ALPHA:
235
return abs(static_cast<int>(ac) - ao) <= threshold;
294
return ((int)abs(check[3] - orig[3]) <= threshold);
236
295
case FLOOD_CHANNELS_R:
237
return abs(static_cast<int>(ac ? unpremul_alpha(rc, ac) : 0) - (ao ? unpremul_alpha(ro, ao) : 0)) <= threshold;
296
return ((int)abs(check[0] - orig[0]) <= threshold);
238
297
case FLOOD_CHANNELS_G:
239
return abs(static_cast<int>(ac ? unpremul_alpha(gc, ac) : 0) - (ao ? unpremul_alpha(go, ao) : 0)) <= threshold;
298
return ((int)abs(check[1] - orig[1]) <= threshold);
240
299
case FLOOD_CHANNELS_B:
241
return abs(static_cast<int>(ac ? unpremul_alpha(bc, ac) : 0) - (ao ? unpremul_alpha(bo, ao) : 0)) <= threshold;
300
return ((int)abs(check[2] - orig[2]) <= threshold);
242
301
case FLOOD_CHANNELS_RGB:
243
guint32 amc, rmc, bmc, gmc;
244
//amc = 255*255 - (255-ac)*(255-ad); amc = (amc + 127) / 255;
245
//amc = (255-ac)*ad + 255*ac; amc = (amc + 127) / 255;
246
amc = 255; // Why are we looking at desktop? Cairo version ignores destop alpha
247
rmc = (255-ac)*rd + 255*rc; rmc = (rmc + 127) / 255;
248
gmc = (255-ac)*gd + 255*gc; gmc = (gmc + 127) / 255;
249
bmc = (255-ac)*bd + 255*bc; bmc = (bmc + 127) / 255;
251
diff += abs(static_cast<int>(amc ? unpremul_alpha(rmc, amc) : 0) - (amop ? unpremul_alpha(rmop, amop) : 0));
252
diff += abs(static_cast<int>(amc ? unpremul_alpha(gmc, amc) : 0) - (amop ? unpremul_alpha(gmop, amop) : 0));
253
diff += abs(static_cast<int>(amc ? unpremul_alpha(bmc, amc) : 0) - (amop ? unpremul_alpha(bmop, amop) : 0));
302
unsigned char merged_check[3];
304
merge_pixel_with_background(check, dtc, merged_check);
306
for (int i = 0; i < 3; i++) {
307
diff += (int)abs(merged_check[i] - merged_orig_pixel[i]);
254
309
return ((diff / 3) <= ((threshold * 3) / 4));
256
311
case FLOOD_CHANNELS_H:
700
* Sort the rendered pixel buffer check queue vertically.
750
* \brief Sort the rendered pixel buffer check queue vertically.
702
752
static bool sort_fill_queue_vertical(Geom::Point a, Geom::Point b) {
703
753
return a[Geom::Y] > b[Geom::Y];
707
* Sort the rendered pixel buffer check queue horizontally.
757
* \brief Sort the rendered pixel buffer check queue horizontally.
709
759
static bool sort_fill_queue_horizontal(Geom::Point a, Geom::Point b) {
710
760
return a[Geom::X] > b[Geom::X];
714
* Perform a flood fill operation.
715
* @param event_context The event context for this tool.
716
* @param event The details of this event.
717
* @param union_with_selection If true, union the new fill with the current selection.
718
* @param is_point_fill If false, use the Rubberband "touch selection" to get the initial points for the fill.
719
* @param is_touch_fill If true, use only the initial contact point in the Rubberband "touch selection" as the fill target color.
764
* \brief Perform a flood fill operation.
765
* \param event_context The event context for this tool.
766
* \param event The details of this event.
767
* \param union_with_selection If true, union the new fill with the current selection.
768
* \param is_point_fill If false, use the Rubberband "touch selection" to get the initial points for the fill.
769
* \param is_touch_fill If true, use only the initial contact point in the Rubberband "touch selection" as the fill target color.
721
static void sp_flood_do_flood_fill(ToolBase *event_context, GdkEvent *event, bool union_with_selection, bool is_point_fill, bool is_touch_fill) {
771
static void sp_flood_do_flood_fill(SPEventContext *event_context, GdkEvent *event, bool union_with_selection, bool is_point_fill, bool is_touch_fill) {
722
772
SPDesktop *desktop = event_context->desktop;
723
SPDocument *document = desktop->getDocument();
725
document->ensureUpToDate();
773
SPDocument *document = sp_desktop_document(desktop);
775
/* Create new arena */
776
NRArena *arena = NRArena::create();
777
unsigned dkey = sp_item_display_key_new(1);
779
sp_document_ensure_up_to_date (document);
727
Geom::OptRect bbox = document->getRoot()->visualBounds();
781
SPItem *document_root = SP_ITEM(SP_DOCUMENT_ROOT(document));
782
Geom::OptRect bbox = document_root->getBounds(Geom::identity());
730
785
desktop->messageStack()->flash(Inkscape::WARNING_MESSAGE, _("<b>Area is not bounded</b>, cannot fill."));
743
798
unsigned int height = (int)ceil(screen.height() * zoom_scale * padding);
745
800
Geom::Point origin(screen.min()[Geom::X],
746
document->getHeight().value("px") - screen.height() - screen.min()[Geom::Y]);
801
sp_document_height(document) - screen.height() - screen.min()[Geom::Y]);
748
origin[Geom::X] += (screen.width() * ((1 - padding) / 2));
749
origin[Geom::Y] += (screen.height() * ((1 - padding) / 2));
803
origin[Geom::X] = origin[Geom::X] + (screen.width() * ((1 - padding) / 2));
804
origin[Geom::Y] = origin[Geom::Y] + (screen.height() * ((1 - padding) / 2));
751
806
Geom::Scale scale(zoom_scale, zoom_scale);
752
Geom::Affine affine = scale * Geom::Translate(-origin * scale);
754
int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, width);
755
guchar *px = g_new(guchar, stride * height);
756
guint32 bgcolor, dtc;
758
// Draw image into data block px
759
{ // this block limits the lifetime of Drawing and DrawingContext
760
/* Create DrawingItems and set transform */
761
unsigned dkey = SPItem::display_key_new(1);
762
Inkscape::Drawing drawing;
763
Inkscape::DrawingItem *root = document->getRoot()->invoke_show( drawing, dkey, SP_ITEM_SHOW_DISPLAY);
764
root->setTransform(affine);
765
drawing.setRoot(root);
767
Geom::IntRect final_bbox = Geom::IntRect::from_xywh(0, 0, width, height);
768
drawing.update(final_bbox);
770
cairo_surface_t *s = cairo_image_surface_create_for_data(
771
px, CAIRO_FORMAT_ARGB32, width, height, stride);
772
Inkscape::DrawingContext dc(s, Geom::Point(0,0));
773
// cairo_translate not necessary here - surface origin is at 0,0
775
SPNamedView *nv = desktop->getNamedView();
776
bgcolor = nv->pagecolor;
777
// bgcolor is 0xrrggbbaa, we need 0xaarrggbb
778
dtc = (bgcolor >> 8) | (bgcolor << 24);
780
dc.setSource(bgcolor);
781
dc.setOperator(CAIRO_OPERATOR_SOURCE);
783
dc.setOperator(CAIRO_OPERATOR_OVER);
785
drawing.render(dc, final_bbox);
787
//cairo_surface_write_to_png( s, "cairo.png" );
789
cairo_surface_flush(s);
790
cairo_surface_destroy(s);
793
document->getRoot()->invoke_hide(dkey);
807
Geom::Matrix affine = scale * Geom::Translate(-origin * scale);
809
/* Create ArenaItems and set transform */
810
NRArenaItem *root = sp_item_invoke_show(SP_ITEM(sp_document_root(document)), arena, dkey, SP_ITEM_SHOW_DISPLAY);
811
nr_arena_item_set_transform(NR_ARENA_ITEM(root), affine);
814
gc.transform.setIdentity();
818
final_bbox.y0 = 0; //row;
819
final_bbox.x1 = width;
820
final_bbox.y1 = height; //row + num_rows;
822
nr_arena_item_invoke_update(root, &final_bbox, &gc, NR_ARENA_ITEM_STATE_ALL, NR_ARENA_ITEM_STATE_NONE);
824
guchar *px = g_new(guchar, 4 * width * height);
827
nr_pixblock_setup_extern( &B, NR_PIXBLOCK_MODE_R8G8B8A8N,
828
final_bbox.x0, final_bbox.y0, final_bbox.x1, final_bbox.y1,
829
px, 4 * width, FALSE, FALSE );
831
SPNamedView *nv = sp_desktop_namedview(desktop);
832
unsigned long bgcolor = nv->pagecolor;
834
unsigned char dtc[4];
835
dtc[0] = NR_RGBA32_R(bgcolor);
836
dtc[1] = NR_RGBA32_G(bgcolor);
837
dtc[2] = NR_RGBA32_B(bgcolor);
838
dtc[3] = NR_RGBA32_A(bgcolor);
840
for (unsigned int fy = 0; fy < height; fy++) {
841
guchar *p = NR_PIXBLOCK_PX(&B) + fy * B.rs;
842
for (unsigned int fx = 0; fx < width; fx++) {
843
for (int i = 0; i < 4; i++) {
797
// // Dump data to png
798
// cairo_surface_t *s = cairo_image_surface_create_for_data(
799
// px, CAIRO_FORMAT_ARGB32, width, height, stride);
800
// cairo_surface_write_to_png( s, "cairo2.png" );
801
// std::cout << " Wrote cairo2.png" << std::endl;
849
nr_arena_item_invoke_render(NULL, root, &final_bbox, &B, NR_ARENA_ITEM_RENDER_NO_CACHE );
850
nr_pixblock_release(&B);
853
sp_item_invoke_hide(SP_ITEM(sp_document_root(document)), dkey);
855
nr_object_unref((NRObject *) arena);
804
857
guchar *trace_px = g_new(guchar, width * height);
805
858
memset(trace_px, 0x00, width * height);
1054
1113
Geom::Point min_start = Geom::Point(min_x, min_y);
1056
1115
affine = scale * Geom::Translate(-origin * scale - min_start);
1057
Geom::Affine inverted_affine = Geom::Affine(affine).inverse();
1116
Geom::Matrix inverted_affine = Geom::Matrix(affine).inverse();
1059
1118
do_trace(bci, trace_px, desktop, inverted_affine, min_x, max_x, min_y, max_y, union_with_selection);
1061
1120
g_free(trace_px);
1063
DocumentUndo::done(document, SP_VERB_CONTEXT_PAINTBUCKET, _("Fill bounded area"));
1122
sp_document_done(document, SP_VERB_CONTEXT_PAINTBUCKET, _("Fill bounded area"));
1066
bool FloodTool::item_handler(SPItem* item, GdkEvent* event) {
1125
static gint sp_flood_context_item_handler(SPEventContext *event_context, SPItem *item, GdkEvent *event)
1067
1127
gint ret = FALSE;
1129
SPDesktop *desktop = event_context->desktop;
1069
1131
switch (event->type) {
1070
1132
case GDK_BUTTON_PRESS:
1071
if ((event->button.state & GDK_CONTROL_MASK) && event->button.button == 1 && !this->space_panning) {
1072
Geom::Point const button_w(event->button.x, event->button.y);
1133
if ((event->button.state & GDK_CONTROL_MASK) && event->button.button == 1 && !event_context->space_panning) {
1134
Geom::Point const button_w(event->button.x,
1074
1137
SPItem *item = sp_event_context_find_item (desktop, button_w, TRUE, TRUE);
1077
desktop->applyCurrentOrToolStyle(item, "/tools/paintbucket", false);
1079
DocumentUndo::done(desktop->getDocument(), SP_VERB_CONTEXT_PAINTBUCKET, _("Set style on object"));
1080
// Dead assignment: Value stored to 'ret' is never read
1139
Inkscape::XML::Node *pathRepr = SP_OBJECT_REPR(item);
1141
sp_desktop_apply_style_tool (desktop, pathRepr, "/tools/paintbucket", false);
1142
sp_document_done(sp_desktop_document(desktop), SP_VERB_CONTEXT_PAINTBUCKET, _("Set style on object"));
1089
// if (((ToolBaseClass *) sp_flood_context_parent_class)->item_handler) {
1090
// ret = ((ToolBaseClass *) sp_flood_context_parent_class)->item_handler(event_context, item, event);
1092
// CPPIFY: ret is overwritten...
1093
ret = ToolBase::item_handler(item, event);
1150
if (((SPEventContextClass *) parent_class)->item_handler) {
1151
ret = ((SPEventContextClass *) parent_class)->item_handler(event_context, item, event);
1098
bool FloodTool::root_handler(GdkEvent* event) {
1157
static gint sp_flood_context_root_handler(SPEventContext *event_context, GdkEvent *event)
1099
1159
static bool dragging;
1101
1161
gint ret = FALSE;
1162
SPDesktop *desktop = event_context->desktop;
1103
1164
switch (event->type) {
1104
1165
case GDK_BUTTON_PRESS:
1105
if (event->button.button == 1 && !this->space_panning) {
1166
if (event->button.button == 1 && !event_context->space_panning) {
1106
1167
if (!(event->button.state & GDK_CONTROL_MASK)) {
1107
Geom::Point const button_w(event->button.x, event->button.y);
1168
Geom::Point const button_w(event->button.x,
1109
if (Inkscape::have_viable_layer(desktop, this->defaultMessageContext())) {
1171
if (Inkscape::have_viable_layer(desktop, event_context->defaultMessageContext())) {
1110
1172
// save drag origin
1111
this->xp = (gint) button_w[Geom::X];
1112
this->yp = (gint) button_w[Geom::Y];
1113
this->within_tolerance = true;
1173
event_context->xp = (gint) button_w[Geom::X];
1174
event_context->yp = (gint) button_w[Geom::Y];
1175
event_context->within_tolerance = true;
1115
1177
dragging = true;
1124
1185
case GDK_MOTION_NOTIFY:
1125
if ( dragging && ( event->motion.state & GDK_BUTTON1_MASK ) && !this->space_panning) {
1126
if ( this->within_tolerance
1127
&& ( abs( (gint) event->motion.x - this->xp ) < this->tolerance )
1128
&& ( abs( (gint) event->motion.y - this->yp ) < this->tolerance ) ) {
1187
&& ( event->motion.state & GDK_BUTTON1_MASK ) && !event_context->space_panning)
1189
if ( event_context->within_tolerance
1190
&& ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance )
1191
&& ( abs( (gint) event->motion.y - event_context->yp ) < event_context->tolerance ) ) {
1129
1192
break; // do not drag if we're within tolerance from origin
1132
this->within_tolerance = false;
1195
event_context->within_tolerance = false;
1134
1197
Geom::Point const motion_pt(event->motion.x, event->motion.y);
1135
1198
Geom::Point const p(desktop->w2d(motion_pt));
1137
1199
if (Inkscape::Rubberband::get(desktop)->is_started()) {
1138
1200
Inkscape::Rubberband::get(desktop)->move(p);
1139
this->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> areas to add to fill, hold <b>Alt</b> for touch fill"));
1201
event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw over</b> areas to add to fill, hold <b>Alt</b> for touch fill"));
1140
1202
gobble_motion_events(GDK_BUTTON1_MASK);
1145
1207
case GDK_BUTTON_RELEASE:
1146
if (event->button.button == 1 && !this->space_panning) {
1147
Inkscape::Rubberband *r = Inkscape::Rubberband::get(desktop);
1208
if (event->button.button == 1 && !event_context->space_panning) {
1209
Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(desktop);
1149
1210
if (r->is_started()) {
1150
1211
// set "busy" cursor
1151
1212
desktop->setWaitingCursor();
1153
if (SP_IS_EVENT_CONTEXT(this)) {
1214
if (SP_IS_EVENT_CONTEXT(event_context)) {
1154
1215
// Since setWaitingCursor runs main loop iterations, we may have already left this tool!
1155
1216
// So check if the tool is valid before doing anything
1156
1217
dragging = false;
1158
bool is_point_fill = this->within_tolerance;
1219
bool is_point_fill = event_context->within_tolerance;
1159
1220
bool is_touch_fill = event->button.state & GDK_MOD1_MASK;
1161
sp_flood_do_flood_fill(this, event, event->button.state & GDK_SHIFT_MASK, is_point_fill, is_touch_fill);
1222
sp_flood_do_flood_fill(event_context, event, event->button.state & GDK_SHIFT_MASK, is_point_fill, is_touch_fill);
1163
1224
desktop->clearWaitingCursor();
1164
1225
// restore cursor when done; note that it may already be different if e.g. user