104
120
delete rc->_message_context;
123
rc->selcon->disconnect();
125
rc->subselcon->disconnect();
126
delete rc->subselcon;
107
128
G_OBJECT_CLASS(parent_class)->dispose(object);
131
const gchar *gr_handle_descr [] = {
132
N_("Linear gradient <b>start</b>"), //POINT_LG_BEGIN
133
N_("Linear gradient <b>end</b>"),
134
N_("Linear gradient <b>mid stop</b>"),
135
N_("Radial gradient <b>center</b>"),
136
N_("Radial gradient <b>radius</b>"),
137
N_("Radial gradient <b>radius</b>"),
138
N_("Radial gradient <b>focus</b>"), // POINT_RG_FOCUS
139
N_("Radial gradient <b>mid stop</b>"),
140
N_("Radial gradient <b>mid stop</b>")
144
gradient_selection_changed (Inkscape::Selection *, gpointer data)
146
SPGradientContext *rc = (SPGradientContext *) data;
148
GrDrag *drag = rc->_grdrag;
149
Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(rc)->desktop);
150
guint n_obj = g_slist_length((GSList *) selection->itemList());
152
if (!drag->isNonEmpty() || selection->isEmpty())
154
guint n_tot = drag->numDraggers();
155
guint n_sel = drag->numSelected();
158
if (drag->singleSelectedDraggerNumDraggables() == 1) {
159
rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
160
_("%s selected out of %d gradient handles on %d selected object(s)"), gr_handle_descr[drag->singleSelectedDraggerSingleDraggableType()], n_tot, n_obj);
162
rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
163
_("One handle merging %d stops (drag with <b>Shift</b> to separate) selected out of %d gradient handles on %d selected object(s)"), drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj);
165
} else if (n_sel > 1) {
166
rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
167
_("<b>%d</b> gradient handles selected out of %d on %d selected object(s)"), n_sel, n_tot, n_obj);
168
} else if (n_sel == 0) {
169
rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
170
_("<b>No</b> gradient handles selected out of %d on %d selected object(s)"), n_tot, n_obj);
175
gradient_subselection_changed (gpointer, gpointer data)
177
gradient_selection_changed (NULL, data);
110
181
static void sp_gradient_context_setup(SPEventContext *ec)
112
183
SPGradientContext *rc = SP_GRADIENT_CONTEXT(ec);
122
193
ec->enableGrDrag();
194
Inkscape::Selection *selection = sp_desktop_selection(ec->desktop);
124
196
rc->_message_context = new Inkscape::MessageContext(sp_desktop_message_stack(ec->desktop));
198
rc->selcon = new sigc::connection (selection->connectChanged( sigc::bind (sigc::ptr_fun(&gradient_selection_changed), rc)));
199
rc->subselcon = new sigc::connection (ec->desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(&gradient_subselection_changed), rc)));
200
gradient_selection_changed(selection, rc);
128
204
sp_gradient_context_select_next (SPEventContext *event_context)
130
206
GrDrag *drag = event_context->_grdrag;
209
GrDragger *d = drag->select_next();
211
event_context->desktop->scroll_to_point(&(d->point), 1.0);
137
215
sp_gradient_context_select_prev (SPEventContext *event_context)
139
217
GrDrag *drag = event_context->_grdrag;
145
static gint sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event)
220
GrDragger *d = drag->select_prev();
222
event_context->desktop->scroll_to_point(&(d->point), 1.0);
226
sp_gradient_context_is_over_line (SPGradientContext *rc, SPItem *item, NR::Point event_p)
228
SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop;
230
//Translate mouse point into proper coord system
231
rc->mousepoint_doc = desktop->w2d(event_p);
233
SPCtrlLine* line = SP_CTRLLINE(item);
235
NR::Point nearest = snap_vector_midpoint (rc->mousepoint_doc, line->s, line->e, 0);
236
double dist_screen = NR::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom();
238
double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance;
240
bool close = (dist_screen < tolerance);
245
std::vector<NR::Point>
246
sp_gradient_context_get_stop_intervals (GrDrag *drag, GSList **these_stops, GSList **next_stops)
248
std::vector<NR::Point> coords;
250
// for all selected draggers
251
for (GList *i = drag->selected; i != NULL; i = i->next) {
252
GrDragger *dragger = (GrDragger *) i->data;
253
// remember the coord of the dragger to reselect it later
254
coords.push_back(dragger->point);
255
// for all draggables of dragger
256
for (GSList const* j = dragger->draggables; j != NULL; j = j->next) {
257
GrDraggable *d = (GrDraggable *) j->data;
260
SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke);
261
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
263
// these draggable types cannot have a next draggabe to insert a stop between them
264
if (d->point_type == POINT_LG_END ||
265
d->point_type == POINT_RG_FOCUS ||
266
d->point_type == POINT_RG_R1 ||
267
d->point_type == POINT_RG_R2) {
271
// from draggables to stops
272
SPStop *this_stop = sp_get_stop_i (vector, d->point_i);
273
SPStop *next_stop = sp_next_stop (this_stop);
274
SPStop *last_stop = sp_last_stop (vector);
276
gint fs = d->fill_or_stroke;
277
SPItem *item = d->item;
278
gint type = d->point_type;
279
gint p_i = d->point_i;
281
// if there's a next stop,
283
GrDragger *dnext = NULL;
285
// (complex because it may have different types, and because in radial,
286
// more than one dragger may correspond to a stop, so we must distinguish)
287
if (type == POINT_LG_BEGIN || type == POINT_LG_MID) {
288
if (next_stop == last_stop)
289
dnext = drag->getDraggerFor (item, POINT_LG_END, p_i+1, fs);
291
dnext = drag->getDraggerFor (item, POINT_LG_MID, p_i+1, fs);
293
if (type == POINT_RG_CENTER || type == POINT_RG_MID1) {
294
if (next_stop == last_stop)
295
dnext = drag->getDraggerFor (item, POINT_RG_R1, p_i+1, fs);
297
dnext = drag->getDraggerFor (item, POINT_RG_MID1, p_i+1, fs);
299
if ((type == POINT_RG_MID2) ||
300
(type == POINT_RG_CENTER && dnext && !dnext->isSelected())) {
301
if (next_stop == last_stop)
302
dnext = drag->getDraggerFor (item, POINT_RG_R2, p_i+1, fs);
304
dnext = drag->getDraggerFor (item, POINT_RG_MID2, p_i+1, fs);
308
// if both adjacent draggers selected,
309
if (!g_slist_find(*these_stops, this_stop) && dnext && dnext->isSelected()) {
311
// remember the coords of the future dragger to select it
312
coords.push_back(0.5*(dragger->point + dnext->point));
314
// do not insert a stop now, it will confuse the loop;
315
// just remember the stops
316
*these_stops = g_slist_prepend (*these_stops, this_stop);
317
*next_stops = g_slist_prepend (*next_stops, next_stop);
326
sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc)
328
SPDocument *doc = NULL;
329
GrDrag *drag = rc->_grdrag;
331
GSList *these_stops = NULL;
332
GSList *next_stops = NULL;
334
std::vector<NR::Point> coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops);
336
if (g_slist_length(these_stops) == 0 && drag->numSelected() == 1) {
337
// if a single stop is selected, add between that stop and the next one
338
GrDragger *dragger = (GrDragger *) drag->selected->data;
339
for (GSList const* j = dragger->draggables; j != NULL; j = j->next) {
340
GrDraggable *d = (GrDraggable *) j->data;
341
SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke);
342
SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false);
343
SPStop *this_stop = sp_get_stop_i (vector, d->point_i);
344
SPStop *next_stop = sp_next_stop (this_stop);
345
if (this_stop && next_stop) {
346
these_stops = g_slist_prepend (these_stops, this_stop);
347
next_stops = g_slist_prepend (next_stops, next_stop);
352
// now actually create the new stops
353
GSList *i = these_stops;
354
GSList *j = next_stops;
355
for (; i != NULL && j != NULL; i = i->next, j = j->next) {
356
SPStop *this_stop = (SPStop *) i->data;
357
SPStop *next_stop = (SPStop *) j->data;
358
gfloat offset = 0.5*(this_stop->offset + next_stop->offset);
359
SPObject *parent = SP_OBJECT_PARENT(this_stop);
360
if (SP_IS_GRADIENT (parent)) {
361
doc = SP_OBJECT_DOCUMENT (parent);
362
sp_vector_add_stop (SP_GRADIENT (parent), this_stop, next_stop, offset);
363
sp_gradient_ensure_vector (SP_GRADIENT (parent));
367
if (g_slist_length(these_stops) > 0 && doc) {
368
sp_document_done (doc, SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop"));
369
drag->updateDraggers();
370
// so that it does not automatically update draggers in idle loop, as this would deselect
371
drag->local_change = true;
372
// select all the old selected and new created draggers
373
drag->selectByCoords(coords);
376
g_slist_free (these_stops);
377
g_slist_free (next_stops);
380
double sqr(double x) {return x*x;}
383
sp_gradient_simplify(SPGradientContext *rc, double tolerance)
385
SPDocument *doc = NULL;
386
GrDrag *drag = rc->_grdrag;
388
GSList *these_stops = NULL;
389
GSList *next_stops = NULL;
391
std::vector<NR::Point> coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops);
393
GSList *todel = NULL;
395
GSList *i = these_stops;
396
GSList *j = next_stops;
397
for (; i != NULL && j != NULL; i = i->next, j = j->next) {
398
SPStop *stop0 = (SPStop *) i->data;
399
SPStop *stop1 = (SPStop *) j->data;
401
gint i1 = g_slist_index(these_stops, stop1);
403
GSList *next_next = g_slist_nth (next_stops, i1);
405
SPStop *stop2 = (SPStop *) next_next->data;
407
if (g_slist_find(todel, stop0) || g_slist_find(todel, stop2))
410
guint32 const c0 = sp_stop_get_rgba32(stop0);
411
guint32 const c2 = sp_stop_get_rgba32(stop2);
412
guint32 const c1r = sp_stop_get_rgba32(stop1);
413
guint32 c1 = average_color (c0, c2,
414
(stop1->offset - stop0->offset) / (stop2->offset - stop0->offset));
417
sqr(SP_RGBA32_R_F(c1) - SP_RGBA32_R_F(c1r)) +
418
sqr(SP_RGBA32_G_F(c1) - SP_RGBA32_G_F(c1r)) +
419
sqr(SP_RGBA32_B_F(c1) - SP_RGBA32_B_F(c1r)) +
420
sqr(SP_RGBA32_A_F(c1) - SP_RGBA32_A_F(c1r));
422
if (diff < tolerance)
423
todel = g_slist_prepend (todel, stop1);
428
for (i = todel; i != NULL; i = i->next) {
429
SPStop *stop = (SPStop*) i->data;
430
doc = SP_OBJECT_DOCUMENT (stop);
431
Inkscape::XML::Node * parent = SP_OBJECT_REPR(stop)->parent();
432
parent->removeChild(SP_OBJECT_REPR(stop));
435
if (g_slist_length(todel) > 0) {
436
sp_document_done (doc, SP_VERB_CONTEXT_GRADIENT, _("Simplify gradient"));
437
drag->local_change = true;
438
drag->updateDraggers();
439
drag->selectByCoords(coords);
442
g_slist_free (todel);
443
g_slist_free (these_stops);
444
g_slist_free (next_stops);
449
sp_gradient_context_add_stop_near_point (SPGradientContext *rc, SPItem *item, NR::Point mouse_p, guint32 /*etime*/)
451
// item is the selected item. mouse_p the location in doc coordinates of where to add the stop
453
SPEventContext *ec = SP_EVENT_CONTEXT(rc);
454
SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop;
456
double tolerance = (double) ec->tolerance;
458
ec->get_drag()->addStopNearPoint (item, mouse_p, tolerance/desktop->current_zoom());
460
sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT,
461
_("Add gradient stop"));
463
ec->get_drag()->updateDraggers();
468
sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event)
147
470
static bool dragging;
161
484
switch (event->type) {
162
485
case GDK_2BUTTON_PRESS:
163
486
if ( event->button.button == 1 ) {
164
for (GSList const* i = selection->itemList(); i != NULL; i = i->next) {
165
SPItem *item = SP_ITEM(i->data);
166
SPGradientType new_type = (SPGradientType) prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR);
167
guint new_fill = prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1);
169
SPGradient *vector = sp_gradient_vector_for_object(sp_desktop_document(desktop), desktop, SP_OBJECT (item), new_fill);
171
SPGradient *priv = sp_item_set_gradient(item, vector, new_type, new_fill);
172
sp_gradient_reset_to_userspace(priv, item);
175
sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT,
176
_("Create default gradient"));
487
bool over_line = false;
488
SPCtrlLine *line = NULL;
490
for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) {
491
line = (SPCtrlLine*) l->data;
492
over_line |= sp_gradient_context_is_over_line (rc, (SPItem*) line, NR::Point(event->motion.x, event->motion.y));
496
// we take the first item in selection, because with doubleclick, the first click
497
// always resets selection to the single object under cursor
498
sp_gradient_context_add_stop_near_point(rc, SP_ITEM(selection->itemList()->data), rc->mousepoint_doc, event->button.time);
500
for (GSList const* i = selection->itemList(); i != NULL; i = i->next) {
501
SPItem *item = SP_ITEM(i->data);
502
SPGradientType new_type = (SPGradientType) prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR);
503
guint new_fill = prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1);
505
SPGradient *vector = sp_gradient_vector_for_object(sp_desktop_document(desktop), desktop, SP_OBJECT (item), new_fill);
507
SPGradient *priv = sp_item_set_gradient(item, vector, new_type, new_fill);
508
sp_gradient_reset_to_userspace(priv, item);
511
sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT,
512
_("Create default gradient"));
181
517
case GDK_BUTTON_PRESS:
182
if ( event->button.button == 1 ) {
518
if ( event->button.button == 1 && !event_context->space_panning ) {
183
519
NR::Point const button_w(event->button.x, event->button.y);
185
521
// save drag origin
220
560
event->motion.y);
221
561
NR::Point const motion_dt = event_context->desktop->w2d(motion_w);
223
sp_gradient_drag(*rc, motion_dt, event->motion.state, event->motion.time);
563
if (Inkscape::Rubberband::get()->is_started()) {
564
Inkscape::Rubberband::get()->move(motion_dt);
565
event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("<b>Draw around</b> handles to select them"));
567
sp_gradient_drag(*rc, motion_dt, event->motion.state, event->motion.time);
569
gobble_motion_events(GDK_BUTTON1_MASK);
573
bool over_line = false;
575
for (GSList *l = drag->lines; l != NULL; l = l->next) {
576
over_line |= sp_gradient_context_is_over_line (rc, (SPItem*) l->data, NR::Point(event->motion.x, event->motion.y));
580
if (rc->cursor_addnode && !over_line) {
581
event_context->cursor_shape = cursor_gradient_xpm;
582
sp_event_context_update_cursor(event_context);
583
rc->cursor_addnode = false;
584
} else if (!rc->cursor_addnode && over_line) {
585
event_context->cursor_shape = cursor_gradient_add_xpm;
586
sp_event_context_update_cursor(event_context);
587
rc->cursor_addnode = true;
228
591
case GDK_BUTTON_RELEASE:
229
592
event_context->xp = event_context->yp = 0;
230
if ( event->button.button == 1 ) {
233
// unless clicked with Ctrl (to enable Ctrl+doubleclick)
234
if (event->button.state & GDK_CONTROL_MASK) {
593
if ( event->button.button == 1 && !event_context->space_panning ) {
594
if ( (event->button.state & GDK_CONTROL_MASK) && (event->button.state & GDK_MOD1_MASK ) ) {
595
bool over_line = false;
596
SPCtrlLine *line = NULL;
598
for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) {
599
line = (SPCtrlLine*) l->data;
600
over_line = sp_gradient_context_is_over_line (rc, (SPItem*) line, NR::Point(event->motion.x, event->motion.y));
605
if (over_line && line) {
606
sp_gradient_context_add_stop_near_point(rc, line->item, rc->mousepoint_doc, 0);
612
// unless clicked with Ctrl (to enable Ctrl+doubleclick).
613
if (event->button.state & GDK_CONTROL_MASK) {
618
if (!event_context->within_tolerance) {
619
// we've been dragging, either do nothing (grdrag handles that),
620
// or rubberband-select if we have rubberband
621
Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get();
622
if (r->is_started() && !event_context->within_tolerance) {
623
// this was a rubberband drag
624
if (r->getMode() == RUBBERBAND_MODE_RECT) {
625
NR::Maybe<NR::Rect> const b = r->getRectangle();
626
drag->selectRect(*b);
630
} else if (event_context->item_to_select) {
631
// no dragging, select clicked item if any
632
if (event->button.state & GDK_SHIFT_MASK) {
633
selection->toggle(event_context->item_to_select);
635
selection->set(event_context->item_to_select);
638
// click in an empty space; do the same as Esc
639
if (drag->selected) {
646
event_context->item_to_select = NULL;
239
if (!event_context->within_tolerance) {
240
// we've been dragging, do nothing (grdrag handles that)
241
} else if (event_context->item_to_select) {
242
// no dragging, select clicked item if any
243
if (event->button.state & GDK_SHIFT_MASK) {
244
selection->toggle(event_context->item_to_select);
246
selection->set(event_context->item_to_select);
249
// click in an empty space; do the same as Esc
250
if (drag->selected) {
251
drag->setSelected (NULL);
257
event_context->item_to_select = NULL;
649
Inkscape::Rubberband::get()->stop();
261
652
case GDK_KEY_PRESS: