X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=src%2Fgradient-context.cpp;h=230072e645b5624b35f54946395c280cdfe64a14;hb=b752616506705efd28f22c4d852d198ff40c7026;hp=a30d674e62d869ed363bb7080ad5677e4c043416;hpb=cd93ce9bf0509d74332b70b4d61c6ea23aa96693;p=inkscape.git diff --git a/src/gradient-context.cpp b/src/gradient-context.cpp index a30d674e6..230072e64 100644 --- a/src/gradient-context.cpp +++ b/src/gradient-context.cpp @@ -29,7 +29,6 @@ #include "message-stack.h" #include "pixmaps/cursor-gradient.xpm" #include "pixmaps/cursor-gradient-add.xpm" -#include "pixmaps/cursor-gradient-delete.xpm" #include "gradient-context.h" #include "gradient-chemistry.h" #include @@ -44,7 +43,9 @@ #include "sp-stop.h" #include "svg/css-ostringstream.h" #include "svg/svg-color.h" - +#include "snap.h" +#include "sp-namedview.h" +#include "rubberband.h" @@ -103,7 +104,7 @@ static void sp_gradient_context_init(SPGradientContext *gr_context) event_context->hot_y = 4; event_context->xp = 0; event_context->yp = 0; - event_context->tolerance = 4; + event_context->tolerance = 6; event_context->within_tolerance = false; event_context->item_to_select = NULL; } @@ -119,9 +120,81 @@ static void sp_gradient_context_dispose(GObject *object) delete rc->_message_context; } + rc->selcon->disconnect(); + delete rc->selcon; + rc->subselcon->disconnect(); + delete rc->subselcon; + G_OBJECT_CLASS(parent_class)->dispose(object); } +const gchar *gr_handle_descr [] = { + N_("Linear gradient start"), //POINT_LG_BEGIN + N_("Linear gradient end"), + N_("Linear gradient mid stop"), + N_("Radial gradient center"), + N_("Radial gradient radius"), + N_("Radial gradient radius"), + N_("Radial gradient focus"), // POINT_RG_FOCUS + N_("Radial gradient mid stop"), + N_("Radial gradient mid stop") +}; + +static void +gradient_selection_changed (Inkscape::Selection *, gpointer data) +{ + SPGradientContext *rc = (SPGradientContext *) data; + + GrDrag *drag = rc->_grdrag; + Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(rc)->desktop); + guint n_obj = g_slist_length((GSList *) selection->itemList()); + + if (!drag->isNonEmpty() || selection->isEmpty()) + return; + guint n_tot = drag->numDraggers(); + guint n_sel = drag->numSelected(); + + //The use of ngettext in the following code is intentional even if the English singular form would never be used + if (n_sel == 1) { + if (drag->singleSelectedDraggerNumDraggables() == 1) { + gchar * message = g_strconcat( + //TRANSLATORS: %s will be substituted with the point name (see previous messages); This is part of a compound message + _("%s selected"), + //TRANSLATORS: Mind the space in front. This is part of a compound message + ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE, + message,_(gr_handle_descr[drag->singleSelectedDraggerSingleDraggableType()]), n_tot, n_obj); + } else { + gchar * message = g_strconcat( + //TRANSLATORS: This is a part of a compound message (out of two more indicating: grandint handle count & object count) + ngettext("One handle merging %d stop (drag with Shift to separate) selected", + "One handle merging %d stops (drag with Shift to separate) selected",drag->singleSelectedDraggerNumDraggables()), + ngettext(" out of %d gradient handle"," out of %d gradient handles",n_tot), + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message,drag->singleSelectedDraggerNumDraggables(), n_tot, n_obj); + } + } else if (n_sel > 1) { + //TRANSLATORS: The plural refers to number of selected gradient handles. This is part of a compound message (part two indicates selected object count) + gchar * message = g_strconcat(ngettext("%d gradient handle selected out of %d","%d gradient handles selected out of %d",n_sel), + //TRANSLATORS: Mind the space in front. (Refers to gradient handles selected). This is part of a compound message + ngettext(" on %d selected object"," on %d selected objects",n_obj),NULL); + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE,message, n_sel, n_tot, n_obj); + } else if (n_sel == 0) { + rc->_message_context->setF(Inkscape::NORMAL_MESSAGE, + //TRANSLATORS: The plural refers to number of selected objects + ngettext("No gradient handles selected out of %d on %d selected object", + "No gradient handles selected out of %d on %d selected objects",n_obj), n_tot, n_obj); + } +} + +static void +gradient_subselection_changed (gpointer, gpointer data) +{ + gradient_selection_changed (NULL, data); +} + + static void sp_gradient_context_setup(SPEventContext *ec) { SPGradientContext *rc = SP_GRADIENT_CONTEXT(ec); @@ -135,40 +208,35 @@ static void sp_gradient_context_setup(SPEventContext *ec) } ec->enableGrDrag(); + Inkscape::Selection *selection = sp_desktop_selection(ec->desktop); rc->_message_context = new Inkscape::MessageContext(sp_desktop_message_stack(ec->desktop)); + + rc->selcon = new sigc::connection (selection->connectChanged( sigc::bind (sigc::ptr_fun(&gradient_selection_changed), rc))); + rc->subselcon = new sigc::connection (ec->desktop->connectToolSubselectionChanged(sigc::bind (sigc::ptr_fun(&gradient_subselection_changed), rc))); + gradient_selection_changed(selection, rc); } -void +void sp_gradient_context_select_next (SPEventContext *event_context) { GrDrag *drag = event_context->_grdrag; g_assert (drag); - drag->select_next(); + GrDragger *d = drag->select_next(); + + event_context->desktop->scroll_to_point(&(d->point), 1.0); } -void +void sp_gradient_context_select_prev (SPEventContext *event_context) { GrDrag *drag = event_context->_grdrag; g_assert (drag); - drag->select_prev(); -} + GrDragger *d = drag->select_prev(); -// FIXME: make global function in libnr or somewhere. -static NR::Point -snap_vector_midpoint (NR::Point p, NR::Point begin, NR::Point end) -{ - double length = NR::L2(end - begin); - NR::Point be = (end - begin) / length; - double r = NR::dot(p - begin, be); - - if (r < 0.0) return begin; - if (r > length) return end; - - return (begin + r * be); + event_context->desktop->scroll_to_point(&(d->point), 1.0); } static bool @@ -180,243 +248,240 @@ sp_gradient_context_is_over_line (SPGradientContext *rc, SPItem *item, NR::Point rc->mousepoint_doc = desktop->w2d(event_p); SPCtrlLine* line = SP_CTRLLINE(item); - - NR::Point nearest = snap_vector_midpoint (rc->mousepoint_doc, line->s, line->e); - NR::Point delta = rc->mousepoint_doc - nearest; + + NR::Point nearest = snap_vector_midpoint (rc->mousepoint_doc, line->s, line->e, 0); + double dist_screen = NR::L2 (rc->mousepoint_doc - nearest) * desktop->current_zoom(); double tolerance = (double) SP_EVENT_CONTEXT(rc)->tolerance; - bool close = (NR::L2 (delta) < tolerance); + bool close = (dist_screen < tolerance); return close; } -// Fixme : must be able to put this in a general file. -static guint32 -average_color (guint32 c1, guint32 c2, gdouble p = 0.5) +std::vector +sp_gradient_context_get_stop_intervals (GrDrag *drag, GSList **these_stops, GSList **next_stops) { - guint32 r = (guint32) (SP_RGBA32_R_U (c1) * (1 - p) + SP_RGBA32_R_U (c2) * p); - guint32 g = (guint32) (SP_RGBA32_G_U (c1) * (1 - p) + SP_RGBA32_G_U (c2) * p); - guint32 b = (guint32) (SP_RGBA32_B_U (c1) * (1 - p) + SP_RGBA32_B_U (c2) * p); - guint32 a = (guint32) (SP_RGBA32_A_U (c1) * (1 - p) + SP_RGBA32_A_U (c2) * p); + std::vector coords; + + // for all selected draggers + for (GList *i = drag->selected; i != NULL; i = i->next) { + GrDragger *dragger = (GrDragger *) i->data; + // remember the coord of the dragger to reselect it later + coords.push_back(dragger->point); + // for all draggables of dragger + for (GSList const* j = dragger->draggables; j != NULL; j = j->next) { + GrDraggable *d = (GrDraggable *) j->data; + + // find the gradient + SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke); + SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false); + + // these draggable types cannot have a next draggabe to insert a stop between them + if (d->point_type == POINT_LG_END || + d->point_type == POINT_RG_FOCUS || + d->point_type == POINT_RG_R1 || + d->point_type == POINT_RG_R2) { + continue; + } - return SP_RGBA32_U_COMPOSE (r, g, b, a); -} + // from draggables to stops + SPStop *this_stop = sp_get_stop_i (vector, d->point_i); + SPStop *next_stop = sp_next_stop (this_stop); + SPStop *last_stop = sp_last_stop (vector); + + gint fs = d->fill_or_stroke; + SPItem *item = d->item; + gint type = d->point_type; + gint p_i = d->point_i; + + // if there's a next stop, + if (next_stop) { + GrDragger *dnext = NULL; + // find its dragger + // (complex because it may have different types, and because in radial, + // more than one dragger may correspond to a stop, so we must distinguish) + if (type == POINT_LG_BEGIN || type == POINT_LG_MID) { + if (next_stop == last_stop) + dnext = drag->getDraggerFor (item, POINT_LG_END, p_i+1, fs); + else + dnext = drag->getDraggerFor (item, POINT_LG_MID, p_i+1, fs); + } else { // radial + if (type == POINT_RG_CENTER || type == POINT_RG_MID1) { + if (next_stop == last_stop) + dnext = drag->getDraggerFor (item, POINT_RG_R1, p_i+1, fs); + else + dnext = drag->getDraggerFor (item, POINT_RG_MID1, p_i+1, fs); + } + if ((type == POINT_RG_MID2) || + (type == POINT_RG_CENTER && dnext && !dnext->isSelected())) { + if (next_stop == last_stop) + dnext = drag->getDraggerFor (item, POINT_RG_R2, p_i+1, fs); + else + dnext = drag->getDraggerFor (item, POINT_RG_MID2, p_i+1, fs); + } + } -static double -get_offset_between_points (NR::Point p, NR::Point begin, NR::Point end) -{ - double length = NR::L2(end - begin); - NR::Point be = (end - begin) / length; - double r = NR::dot(p - begin, be); - - if (r < 0.0) return 0.0; - if (r > length) return 1.0; - - return (r / length); -} + // if both adjacent draggers selected, + if (!g_slist_find(*these_stops, this_stop) && dnext && dnext->isSelected()) { -static void -sp_gradient_context_add_stop_near_point (SPGradientContext *rc, SPItem *item, NR::Point mouse_p, guint32 etime) -{ - // item is the selected item. mouse_p the location in doc coordinates of where to add the stop - - SPEventContext *ec = SP_EVENT_CONTEXT(rc); - double tolerance = (double) ec->tolerance; + // remember the coords of the future dragger to select it + coords.push_back(0.5*(dragger->point + dnext->point)); - gfloat offset; // type of SPStop.offset = gfloat - SPGradient *gradient; - bool fill_or_stroke = true; - bool r1_knot = false; - - bool addknot = false; - do { - gradient = sp_item_gradient (item, fill_or_stroke); - if (SP_IS_LINEARGRADIENT(gradient)) { - NR::Point begin = sp_item_gradient_get_coords(item, POINT_LG_BEGIN, 0, fill_or_stroke); - NR::Point end = sp_item_gradient_get_coords(item, POINT_LG_END, 0, fill_or_stroke); - - NR::Point nearest = snap_vector_midpoint (mouse_p, begin, end); - NR::Point delta = mouse_p - nearest; - if ( NR::L2 (delta) < tolerance ) { - // add the knot - offset = get_offset_between_points(nearest, begin, end); - addknot = true; - break; // break out of the while loop: add only one knot - } - } else if (SP_IS_RADIALGRADIENT(gradient)) { - NR::Point begin = sp_item_gradient_get_coords(item, POINT_RG_CENTER, 0, fill_or_stroke); - NR::Point end = sp_item_gradient_get_coords(item, POINT_RG_R1, 0, fill_or_stroke); - NR::Point nearest = snap_vector_midpoint (mouse_p, begin, end); - NR::Point delta = mouse_p - nearest; - if ( NR::L2 (delta) < tolerance ) { - offset = get_offset_between_points(nearest, begin, end); - addknot = true; - r1_knot = true; - break; // break out of the while loop: add only one knot - } - - end = sp_item_gradient_get_coords(item, POINT_RG_R2, 0, fill_or_stroke); - nearest = snap_vector_midpoint (mouse_p, begin, end); - delta = mouse_p - nearest; - if ( NR::L2 (delta) < tolerance ) { - offset = get_offset_between_points(nearest, begin, end); - addknot = true; - r1_knot = false; - break; // break out of the while loop: add only one knot - } - } - fill_or_stroke = !fill_or_stroke; - } while (!fill_or_stroke && !addknot) ; - - if (addknot) { - SPGradient *vector = sp_gradient_get_vector (gradient, false); - SPStop* prev_stop = sp_first_stop(vector); - SPStop* next_stop = sp_next_stop(prev_stop); - while ( (next_stop) && (next_stop->offset < offset) ) { - prev_stop = next_stop; - next_stop = sp_next_stop(next_stop); - } - if (!next_stop) { - // logical error: the endstop should have offset 1 and should always be more than this offset here - return; - } - - Inkscape::XML::Node *new_stop_repr = NULL; - new_stop_repr = SP_OBJECT_REPR(prev_stop)->duplicate(); - SP_OBJECT_REPR(vector)->addChild(new_stop_repr, SP_OBJECT_REPR(prev_stop)); - - SPStop *newstop = (SPStop *) SP_OBJECT_DOCUMENT(vector)->getObjectByRepr(new_stop_repr); - newstop->offset = offset; - sp_repr_set_css_double( SP_OBJECT_REPR(newstop), "offset", (double)offset); - guint32 const c1 = sp_stop_get_rgba32(prev_stop); - guint32 const c2 = sp_stop_get_rgba32(next_stop); - guint32 cnew = average_color (c1, c2, (offset - prev_stop->offset) / (next_stop->offset - prev_stop->offset)); - Inkscape::CSSOStringStream os; - gchar c[64]; - sp_svg_write_color (c, 64, cnew); - gdouble opacity = (gdouble) SP_RGBA32_A_F (cnew); - os << "stop-color:" << c << ";stop-opacity:" << opacity <<";"; - SP_OBJECT_REPR (newstop)->setAttribute("style", os.str().c_str()); - - - Inkscape::GC::release(new_stop_repr); - sp_document_done (SP_OBJECT_DOCUMENT (vector), SP_VERB_CONTEXT_GRADIENT, - _("Add gradient stop")); - - ec->_grdrag->updateDraggers(); - sp_gradient_ensure_vector (gradient); - - if (vector->has_stops) { - int i = 0; - for ( SPObject *ochild = sp_object_first_child (SP_OBJECT(vector)) ; ochild != NULL ; ochild = SP_OBJECT_NEXT(ochild) ) { - if (SP_IS_STOP (ochild)) { - if ( SP_STOP(ochild) == newstop ) { - break; - } else { - i++; - } + // do not insert a stop now, it will confuse the loop; + // just remember the stops + *these_stops = g_slist_prepend (*these_stops, this_stop); + *next_stops = g_slist_prepend (*next_stops, next_stop); } } - GrDragger *dragger = NULL; - gradient = sp_item_gradient (item, fill_or_stroke); - GrPointType pointtype; - if (SP_IS_LINEARGRADIENT(gradient)) { - dragger = SP_EVENT_CONTEXT(rc)->_grdrag->getDraggerFor (item, POINT_LG_MID, i, fill_or_stroke); - pointtype = POINT_LG_MID; - } else if (SP_IS_RADIALGRADIENT(gradient)) { - dragger = SP_EVENT_CONTEXT(rc)->_grdrag->getDraggerFor (item, r1_knot ? POINT_RG_MID1 : POINT_RG_MID2, i, fill_or_stroke); - pointtype = r1_knot ? POINT_RG_MID1 : POINT_RG_MID2; - } - if (dragger && (etime == 0) ) { - ec->_grdrag->setSelected (dragger); - } else { - ec->_grdrag->grabKnot (item, - pointtype, - i, - fill_or_stroke, 99999, 99999, etime); - } - ec->_grdrag->local_change = true; - } } + return coords; } static void -sp_gradient_context_delete_stops (SPGradientContext *rc, SPItem *item, GrDrag *drag, bool just_one) { - GrDragger *draggertemp = (GrDragger*) drag->selected->data; - GrDraggable *draggabletemp = (GrDraggable*) draggertemp->draggables->data; - SPGradient *gradient = sp_item_gradient (item, draggabletemp->fill_or_stroke); - SPGradient *vector = sp_gradient_get_vector (gradient, false); - - // 2 is the minimum, cannot delete more than that without deleting the whole vector - guint num_delete = just_one ? 1 : g_list_length(drag->selected); - if (vector->vector.stops.size() >= (2+num_delete) ) { - GSList *stoplist = NULL; - while (drag->selected) { - GrDragger *dragger = (GrDragger*) drag->selected->data; - GrDraggable *draggable = (GrDraggable*) dragger->draggables->data; - SPStop *selstop = NULL; - switch (draggable->point_type) { - case POINT_LG_END: - case POINT_RG_R1: - case POINT_RG_R2: - selstop = sp_last_stop(vector); - break; - default: - selstop = sp_get_stop_i(vector, draggable->point_i); - break; - } - if ( !g_slist_find(stoplist, selstop) ) { - stoplist = g_slist_append(stoplist, selstop); +sp_gradient_context_add_stops_between_selected_stops (SPGradientContext *rc) +{ + SPDocument *doc = NULL; + GrDrag *drag = rc->_grdrag; + + GSList *these_stops = NULL; + GSList *next_stops = NULL; + + std::vector coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops); + + if (g_slist_length(these_stops) == 0 && drag->numSelected() == 1) { + // if a single stop is selected, add between that stop and the next one + GrDragger *dragger = (GrDragger *) drag->selected->data; + for (GSList const* j = dragger->draggables; j != NULL; j = j->next) { + GrDraggable *d = (GrDraggable *) j->data; + SPGradient *gradient = sp_item_gradient (d->item, d->fill_or_stroke); + SPGradient *vector = sp_gradient_get_forked_vector_if_necessary (gradient, false); + SPStop *this_stop = sp_get_stop_i (vector, d->point_i); + SPStop *next_stop = sp_next_stop (this_stop); + if (this_stop && next_stop) { + these_stops = g_slist_prepend (these_stops, this_stop); + next_stops = g_slist_prepend (next_stops, next_stop); } - drag->selected = g_list_remove(drag->selected, dragger); - if ( just_one ) break; // iterate once if just_one is set. } - while (stoplist) { - SPStop *stop = (SPStop*) stoplist->data; - SP_OBJECT_REPR(vector)->removeChild(SP_OBJECT_REPR(stop)); - stoplist = g_slist_remove(stoplist, stop); + } + + // now actually create the new stops + GSList *i = these_stops; + GSList *j = next_stops; + for (; i != NULL && j != NULL; i = i->next, j = j->next) { + SPStop *this_stop = (SPStop *) i->data; + SPStop *next_stop = (SPStop *) j->data; + gfloat offset = 0.5*(this_stop->offset + next_stop->offset); + SPObject *parent = SP_OBJECT_PARENT(this_stop); + if (SP_IS_GRADIENT (parent)) { + doc = SP_OBJECT_DOCUMENT (parent); + sp_vector_add_stop (SP_GRADIENT (parent), this_stop, next_stop, offset); + sp_gradient_ensure_vector (SP_GRADIENT (parent)); } - // if we delete first or last stop, move the next/previous to the edge - SPStop *first = sp_first_stop (vector); - if (first) { - if (first->offset != 0) { - first->offset = 0; - sp_repr_set_css_double (SP_OBJECT_REPR (first), "offset", 0); - } - } - SPStop *last = sp_last_stop (vector); - if (last) { - if (last->offset != 1) { - last->offset = 1; - sp_repr_set_css_double (SP_OBJECT_REPR (last), "offset", 1); + } + + if (g_slist_length(these_stops) > 0 && doc) { + sp_document_done (doc, SP_VERB_CONTEXT_GRADIENT, _("Add gradient stop")); + drag->updateDraggers(); + // so that it does not automatically update draggers in idle loop, as this would deselect + drag->local_change = true; + // select all the old selected and new created draggers + drag->selectByCoords(coords); + } + + g_slist_free (these_stops); + g_slist_free (next_stops); +} + +double sqr(double x) {return x*x;} + +static void +sp_gradient_simplify(SPGradientContext *rc, double tolerance) +{ + SPDocument *doc = NULL; + GrDrag *drag = rc->_grdrag; + + GSList *these_stops = NULL; + GSList *next_stops = NULL; + + std::vector coords = sp_gradient_context_get_stop_intervals (drag, &these_stops, &next_stops); + + GSList *todel = NULL; + + GSList *i = these_stops; + GSList *j = next_stops; + for (; i != NULL && j != NULL; i = i->next, j = j->next) { + SPStop *stop0 = (SPStop *) i->data; + SPStop *stop1 = (SPStop *) j->data; + + gint i1 = g_slist_index(these_stops, stop1); + if (i1 != -1) { + GSList *next_next = g_slist_nth (next_stops, i1); + if (next_next) { + SPStop *stop2 = (SPStop *) next_next->data; + + if (g_slist_find(todel, stop0) || g_slist_find(todel, stop2)) + continue; + + guint32 const c0 = sp_stop_get_rgba32(stop0); + guint32 const c2 = sp_stop_get_rgba32(stop2); + guint32 const c1r = sp_stop_get_rgba32(stop1); + guint32 c1 = average_color (c0, c2, + (stop1->offset - stop0->offset) / (stop2->offset - stop0->offset)); + + double diff = + sqr(SP_RGBA32_R_F(c1) - SP_RGBA32_R_F(c1r)) + + sqr(SP_RGBA32_G_F(c1) - SP_RGBA32_G_F(c1r)) + + sqr(SP_RGBA32_B_F(c1) - SP_RGBA32_B_F(c1r)) + + sqr(SP_RGBA32_A_F(c1) - SP_RGBA32_A_F(c1r)); + + if (diff < tolerance) + todel = g_slist_prepend (todel, stop1); } - } - if ( just_one || (num_delete == 1) ) { - sp_document_done (SP_OBJECT_DOCUMENT (vector), SP_VERB_CONTEXT_GRADIENT, - _("Delete gradient stop")); - } else { - sp_document_done (SP_OBJECT_DOCUMENT (vector), SP_VERB_CONTEXT_GRADIENT, - _("Delete gradient stops")); } - } else { // delete the gradient from the object. set fill to unset - SPCSSAttr *css = sp_repr_css_attr_new (); - if (draggabletemp->fill_or_stroke) { - sp_repr_css_unset_property (css, "fill"); - } else { - sp_repr_css_unset_property (css, "stroke"); - } - sp_repr_css_change (SP_OBJECT_REPR (item), css, "style"); - sp_repr_css_attr_unref (css); - sp_document_done (SP_OBJECT_DOCUMENT (vector), SP_VERB_CONTEXT_GRADIENT, - _("Remove gradient")); } + + for (i = todel; i != NULL; i = i->next) { + SPStop *stop = (SPStop*) i->data; + doc = SP_OBJECT_DOCUMENT (stop); + Inkscape::XML::Node * parent = SP_OBJECT_REPR(stop)->parent(); + parent->removeChild(SP_OBJECT_REPR(stop)); + } + + if (g_slist_length(todel) > 0) { + sp_document_done (doc, SP_VERB_CONTEXT_GRADIENT, _("Simplify gradient")); + drag->local_change = true; + drag->updateDraggers(); + drag->selectByCoords(coords); + } + + g_slist_free (todel); + g_slist_free (these_stops); + g_slist_free (next_stops); } +static void +sp_gradient_context_add_stop_near_point (SPGradientContext *rc, SPItem *item, NR::Point mouse_p, guint32 /*etime*/) +{ + // item is the selected item. mouse_p the location in doc coordinates of where to add the stop + + SPEventContext *ec = SP_EVENT_CONTEXT(rc); + SPDesktop *desktop = SP_EVENT_CONTEXT (rc)->desktop; + + double tolerance = (double) ec->tolerance; -static gint + ec->get_drag()->addStopNearPoint (item, mouse_p, tolerance/desktop->current_zoom()); + + sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT, + _("Add gradient stop")); + + ec->get_drag()->updateDraggers(); +} + + +static gint sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) { static bool dragging; @@ -440,24 +505,26 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) SPCtrlLine *line = NULL; if (drag->lines) { for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) { - line = (SPCtrlLine*) l->data; + line = (SPCtrlLine*) l->data; over_line |= sp_gradient_context_is_over_line (rc, (SPItem*) line, NR::Point(event->motion.x, event->motion.y)); } } if (over_line) { + // we take the first item in selection, because with doubleclick, the first click + // always resets selection to the single object under cursor sp_gradient_context_add_stop_near_point(rc, SP_ITEM(selection->itemList()->data), rc->mousepoint_doc, event->button.time); } else { for (GSList const* i = selection->itemList(); i != NULL; i = i->next) { SPItem *item = SP_ITEM(i->data); SPGradientType new_type = (SPGradientType) prefs_get_int_attribute ("tools.gradient", "newgradient", SP_GRADIENT_TYPE_LINEAR); guint new_fill = prefs_get_int_attribute ("tools.gradient", "newfillorstroke", 1); - + SPGradient *vector = sp_gradient_vector_for_object(sp_desktop_document(desktop), desktop, SP_OBJECT (item), new_fill); - + SPGradient *priv = sp_item_set_gradient(item, vector, new_type, new_fill); sp_gradient_reset_to_userspace(priv, item); } - + sp_document_done (sp_desktop_document (desktop), SP_VERB_CONTEXT_GRADIENT, _("Create default gradient")); } @@ -465,7 +532,7 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) } break; case GDK_BUTTON_PRESS: - if ( event->button.button == 1 ) { + if ( event->button.button == 1 && !event_context->space_panning ) { NR::Point const button_w(event->button.x, event->button.y); // save drag origin @@ -473,24 +540,28 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) event_context->yp = (gint) button_w[NR::Y]; event_context->within_tolerance = true; - // remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to - // enable Ctrl+doubleclick of exactly the selected item(s) - if (!(event->button.state & GDK_CONTROL_MASK)) - event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE); - dragging = true; - /* Position center */ - NR::Point const button_dt = desktop->w2d(button_w); - /* Snap center to nearest magnetic point */ - rc->origin = button_dt; + NR::Point const button_dt = desktop->w2d(button_w); + if (event->button.state & GDK_SHIFT_MASK) { + Inkscape::Rubberband::get()->start(desktop, button_dt); + } else { + // remember clicked item, disregarding groups, honoring Alt; do nothing with Crtl to + // enable Ctrl+doubleclick of exactly the selected item(s) + if (!(event->button.state & GDK_CONTROL_MASK)) + event_context->item_to_select = sp_event_context_find_item (desktop, button_w, event->button.state & GDK_MOD1_MASK, TRUE); + + /* Snap center to nearest magnetic point */ + SnapManager const &m = desktop->namedview->snap_manager; + rc->origin = m.freeSnap(Inkscape::Snapper::SNAPPOINT_NODE, button_dt, NULL).getPoint(); + } ret = TRUE; } break; case GDK_MOTION_NOTIFY: if ( dragging - && ( event->motion.state & GDK_BUTTON1_MASK ) ) + && ( event->motion.state & GDK_BUTTON1_MASK ) && !event_context->space_panning ) { if ( event_context->within_tolerance && ( abs( (gint) event->motion.x - event_context->xp ) < event_context->tolerance ) @@ -506,7 +577,13 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) event->motion.y); NR::Point const motion_dt = event_context->desktop->w2d(motion_w); - sp_gradient_drag(*rc, motion_dt, event->motion.state, event->motion.time); + if (Inkscape::Rubberband::get()->is_started()) { + Inkscape::Rubberband::get()->move(motion_dt); + event_context->defaultMessageContext()->set(Inkscape::NORMAL_MESSAGE, _("Draw around handles to select them")); + } else { + sp_gradient_drag(*rc, motion_dt, event->motion.state, event->motion.time); + } + gobble_motion_events(GDK_BUTTON1_MASK); ret = TRUE; } else { @@ -530,31 +607,43 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) break; case GDK_BUTTON_RELEASE: event_context->xp = event_context->yp = 0; - if ( event->button.button == 1 ) { + if ( event->button.button == 1 && !event_context->space_panning ) { if ( (event->button.state & GDK_CONTROL_MASK) && (event->button.state & GDK_MOD1_MASK ) ) { bool over_line = false; SPCtrlLine *line = NULL; if (drag->lines) { for (GSList *l = drag->lines; (l != NULL) && (!over_line); l = l->next) { - line = (SPCtrlLine*) l->data; - over_line |= sp_gradient_context_is_over_line (rc, (SPItem*) line, NR::Point(event->motion.x, event->motion.y)); + line = (SPCtrlLine*) l->data; + over_line = sp_gradient_context_is_over_line (rc, (SPItem*) line, NR::Point(event->motion.x, event->motion.y)); + if (over_line) + break; } } - if (over_line) { - sp_gradient_context_add_stop_near_point(rc, SP_ITEM(selection->itemList()->data), rc->mousepoint_doc, 0); + if (over_line && line) { + sp_gradient_context_add_stop_near_point(rc, line->item, rc->mousepoint_doc, 0); ret = TRUE; } - } else { + } else { dragging = false; - - // unless clicked with Ctrl (to enable Ctrl+doubleclick). (don't what this is for (johan)) + + // unless clicked with Ctrl (to enable Ctrl+doubleclick). if (event->button.state & GDK_CONTROL_MASK) { ret = TRUE; break; } - + if (!event_context->within_tolerance) { - // we've been dragging, do nothing (grdrag handles that) + // we've been dragging, either do nothing (grdrag handles that), + // or rubberband-select if we have rubberband + Inkscape::Rubberband::Rubberband *r = Inkscape::Rubberband::get(); + if (r->is_started() && !event_context->within_tolerance) { + // this was a rubberband drag + if (r->getMode() == RUBBERBAND_MODE_RECT) { + NR::Maybe const b = r->getRectangle(); + drag->selectRect(*b); + } + } + } else if (event_context->item_to_select) { // no dragging, select clicked item if any if (event->button.state & GDK_SHIFT_MASK) { @@ -565,15 +654,16 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) } else { // click in an empty space; do the same as Esc if (drag->selected) { - drag->deselect_all(); + drag->deselectAll(); } else { selection->clear(); } } - + event_context->item_to_select = NULL; ret = TRUE; } + Inkscape::Rubberband::get()->stop(); } break; case GDK_KEY_PRESS: @@ -600,9 +690,25 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) } break; + case GDK_A: + case GDK_a: + if (MOD__CTRL_ONLY && drag->isNonEmpty()) { + drag->selectAll(); + ret = TRUE; + } + break; + + case GDK_L: + case GDK_l: + if (MOD__CTRL_ONLY && drag->isNonEmpty() && drag->hasSelection()) { + sp_gradient_simplify(rc, 1e-4); + ret = TRUE; + } + break; + case GDK_Escape: if (drag->selected) { - drag->deselect_all(); + drag->deselectAll(); } else { selection->clear(); } @@ -614,13 +720,15 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_Left: case GDK_KP_4: if (!MOD__CTRL) { // not ctrl + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__ALT) { // alt - if (MOD__SHIFT) drag->selected_move_screen(-10, 0); // shift - else drag->selected_move_screen(-1, 0); // no shift + if (MOD__SHIFT) drag->selected_move_screen(mul*-10, 0); // shift + else drag->selected_move_screen(mul*-1, 0); // no shift } else { // no alt - if (MOD__SHIFT) drag->selected_move(-10*nudge, 0); // shift - else drag->selected_move(-nudge, 0); // no shift + if (MOD__SHIFT) drag->selected_move(mul*-10*nudge, 0); // shift + else drag->selected_move(mul*-nudge, 0); // no shift } ret = TRUE; } @@ -629,13 +737,15 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_Up: case GDK_KP_8: if (!MOD__CTRL) { // not ctrl + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__ALT) { // alt - if (MOD__SHIFT) drag->selected_move_screen(0, 10); // shift - else drag->selected_move_screen(0, 1); // no shift + if (MOD__SHIFT) drag->selected_move_screen(0, mul*10); // shift + else drag->selected_move_screen(0, mul*1); // no shift } else { // no alt - if (MOD__SHIFT) drag->selected_move(0, 10*nudge); // shift - else drag->selected_move(0, nudge); // no shift + if (MOD__SHIFT) drag->selected_move(0, mul*10*nudge); // shift + else drag->selected_move(0, mul*nudge); // no shift } ret = TRUE; } @@ -644,13 +754,15 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_Right: case GDK_KP_6: if (!MOD__CTRL) { // not ctrl + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__ALT) { // alt - if (MOD__SHIFT) drag->selected_move_screen(10, 0); // shift - else drag->selected_move_screen(1, 0); // no shift + if (MOD__SHIFT) drag->selected_move_screen(mul*10, 0); // shift + else drag->selected_move_screen(mul*1, 0); // no shift } else { // no alt - if (MOD__SHIFT) drag->selected_move(10*nudge, 0); // shift - else drag->selected_move(nudge, 0); // no shift + if (MOD__SHIFT) drag->selected_move(mul*10*nudge, 0); // shift + else drag->selected_move(mul*nudge, 0); // no shift } ret = TRUE; } @@ -659,13 +771,15 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) case GDK_KP_Down: case GDK_KP_2: if (!MOD__CTRL) { // not ctrl + gint mul = 1 + gobble_key_events( + get_group0_keyval(&event->key), 0); // with any mask if (MOD__ALT) { // alt - if (MOD__SHIFT) drag->selected_move_screen(0, -10); // shift - else drag->selected_move_screen(0, -1); // no shift + if (MOD__SHIFT) drag->selected_move_screen(0, mul*-10); // shift + else drag->selected_move_screen(0, mul*-1); // no shift } else { // no alt - if (MOD__SHIFT) drag->selected_move(0, -10*nudge); // shift - else drag->selected_move(0, -nudge); // no shift + if (MOD__SHIFT) drag->selected_move(0, mul*-10*nudge); // shift + else drag->selected_move(0, mul*-nudge); // no shift } ret = TRUE; } @@ -688,20 +802,20 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) ret = TRUE; } break; -/* + case GDK_Insert: case GDK_KP_Insert: - // with any modifiers - sp_node_selected_add_node(); + // with any modifiers: + sp_gradient_context_add_stops_between_selected_stops (rc); ret = TRUE; break; -*/ + case GDK_Delete: case GDK_KP_Delete: case GDK_BackSpace: if ( drag->selected ) { - sp_gradient_context_delete_stops ( rc, SP_ITEM(selection->itemList()->data), drag, MOD__CTRL_ONLY ) ; - ret = TRUE; + drag->deleteSelected(MOD__CTRL_ONLY); + ret = TRUE; } break; default: @@ -733,11 +847,11 @@ sp_gradient_context_root_handler(SPEventContext *event_context, GdkEvent *event) ret = ((SPEventContextClass *) parent_class)->root_handler(event_context, event); } } - + return ret; } -static void sp_gradient_drag(SPGradientContext &rc, NR::Point const pt, guint state, guint32 etime) +static void sp_gradient_drag(SPGradientContext &rc, NR::Point const pt, guint /*state*/, guint32 etime) { SPDesktop *desktop = SP_EVENT_CONTEXT(&rc)->desktop; Inkscape::Selection *selection = sp_desktop_selection(desktop); @@ -750,9 +864,16 @@ static void sp_gradient_drag(SPGradientContext &rc, NR::Point const pt, guint st SPGradient *vector; if (ec->item_to_select) { + // pick color from the object where drag started vector = sp_gradient_vector_for_object(document, desktop, ec->item_to_select, fill_or_stroke); } else { - vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(selection->itemList()->data), fill_or_stroke); + // Starting from empty space: + // Sort items so that the topmost comes last + GSList *items = g_slist_copy ((GSList *) selection->itemList()); + items = g_slist_sort(items, (GCompareFunc) sp_item_repr_compare_position); + // take topmost + vector = sp_gradient_vector_for_object(document, desktop, SP_ITEM(g_slist_last(items)->data), fill_or_stroke); + g_slist_free (items); } // HACK: reset fill-opacity - that 0.75 is annoying; BUT remove this when we have an opacity slider for all tabs @@ -784,7 +905,7 @@ static void sp_gradient_drag(SPGradientContext &rc, NR::Point const pt, guint st // and therefore are already out of tolerance ec->_grdrag->grabKnot (SP_ITEM(selection->itemList()->data), type == SP_GRADIENT_TYPE_LINEAR? POINT_LG_END : POINT_RG_R1, - 0, //point_i + -1, // ignore number (though it is always 1) fill_or_stroke, 99999, 99999, etime); } // We did an undoable action, but sp_document_done will be called by the knot when released