gradient-drag.cpp revision 09ba3247163582bf2e30e17c4c154aa259ce038a
0N/A#define __GRADIENT_DRAG_C__
0N/A#ifdef HAVE_CONFIG_H
#include <cstring>
#include <string>
#include "desktop-handles.h"
#include "selection.h"
#include "desktop.h"
#include "desktop-style.h"
#include "document.h"
#include "display/sp-ctrlline.h"
#include "display/sp-canvas-util.h"
#include "svg/css-ostringstream.h"
#include "libnr/nr-point-fns.h"
#include "preferences.h"
#include "sp-item.h"
#include "style.h"
#include "knot.h"
#include "sp-linear-gradient.h"
#include "sp-radial-gradient.h"
#include "gradient-chemistry.h"
#include "gradient-drag.h"
#include "sp-stop.h"
#include "snap.h"
#include "sp-namedview.h"
#include "selection-chemistry.h"
// absolute distance between gradient points for them to become a single dragger when the drag is created:
When a _query_style_signal is received, check that \a property requests fill/stroke/opacity (otherwise
if (property != QUERY_STYLE_PROPERTY_FILL && property != QUERY_STYLE_PROPERTY_STROKE && property != QUERY_STYLE_PROPERTY_MASTEROPACITY) {
return QUERY_STYLE_NOTHING;
return QUERY_STYLE_NOTHING;
int count = 0;
guint32 c = sp_item_gradient_stop_query_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
count ++;
if (count) {
return ret;
if ((css->attribute("fill") && !css->attribute("stroke") && !strcmp(css->attribute("fill"), "none")) ||
sp_repr_css_set_property (stop, "stop-opacity", "0"); // if a single fill/stroke property is set to none, don't change color, set opacity to 0
for (GList const* sel = drag->selected; sel != NULL; sel = sel->next) { // for all selected draggers
for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop);
if (!selected) return 0;
int count = 0;
guint32 c = sp_item_gradient_stop_query_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
count ++;
if (count) {
SPStop *
bool fill_or_stroke = true;
bool r1_knot = false;
bool addknot = false;
addknot = true;
addknot = true;
r1_knot = true;
addknot = true;
r1_knot = false;
if (addknot) {
if (!next_stop) {
return NULL;
return newstop;
return NULL;
local_change = true;
sp_item_gradient_stop_set_style (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke, stop);
bool over_line = false;
if (lines) {
if (stop) {
this->local_change = false;
(gpointer)this )
(gpointer)this )
(gpointer)this )
(gpointer)this )
this->updateDraggers ();
this->updateLines ();
this->updateLevels ();
this->setSelected (getDraggerFor (desktop->gr_item, desktop->gr_point_type, desktop->gr_point_i, desktop->gr_fill_or_stroke));
if (this->selected) {
deselect_all();
SPObject *
if (!item)
return NULL;
if (fill_or_stroke)
return server;
double r = L2 (p - o);
for (GSList const* i = dragger->draggables; i != NULL; i = i->next) { // for all draggables of dragger
GrDraggable *da_new = new GrDraggable (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
delete dragger;
Inkscape::SnappedPoint s = m.freeSnap(Inkscape::SnapPreferences::SNAPPOINT_NODE, to_2geom(p), Inkscape::SNAPSOURCE_HANDLE);
if (s.getSnapped()) {
p = s.getPoint();
} else if (m.snapprefs.getSnapEnabledGlobally() && m.snapprefs.getSnapModeNode() && !(m.snapprefs.getSnapPostponedGlobally())) {
bool was_snapped = false;
s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false);
was_snapped = true;
s = Inkscape::SnappedPoint(p, Inkscape::SNAPSOURCE_HANDLE, Inkscape::SNAPTARGET_GRADIENTS_PARENT_BBOX, dist, snap_dist, false, false);
was_snapped = true;
if (was_snapped) {
} else if (draggable->point_type == POINT_RG_R1 || draggable->point_type == POINT_RG_R2 || draggable->point_type == POINT_RG_FOCUS) {
snap_vector = get_snap_vector (p, dr_snap, M_PI/2, Geom::atan2 (dragger->point_original - dr_snap));
if (snap_vector) {
p += move;
gr_midpoint_limits(GrDragger *dragger, SPObject *server, Geom::Point *begin, Geom::Point *end, Geom::Point *low_lim, Geom::Point *high_lim, GSList **moving)
d_add = drag->getDraggerFor(draggable->item, draggable->point_type, lowest_i - 1, draggable->fill_or_stroke);
d_add = drag->getDraggerFor(draggable->item, draggable->point_type, highest_i + 1, draggable->fill_or_stroke);
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_MID, lowest_i - 1, draggable->fill_or_stroke);
if (d_temp)
d_temp = drag->getDraggerFor (draggable->item, POINT_LG_MID, highest_i + 1, draggable->fill_or_stroke);
if (d_temp)
d_temp = drag->getDraggerFor (draggable->item, draggable->point_type, lowest_i - 1, draggable->fill_or_stroke);
if (d_temp)
d_temp = drag->getDraggerFor (draggable->item, draggable->point_type, highest_i + 1, draggable->fill_or_stroke);
d_temp = drag->getDraggerFor (draggable->item, (draggable->point_type==POINT_RG_MID1) ? POINT_RG_R1 : POINT_RG_R2, num-1, draggable->fill_or_stroke);
if (d_temp)
gr_knot_moved_midpoint_handler(SPKnot */*knot*/, Geom::Point const &ppointer, guint state, gpointer data)
if (Geom::L2(drg->point - dragger->point) + Geom::L2(drg->point - begin) - 1e-3 > Geom::L2(dragger->point - begin)) { // drg is on the end side from dragger
Called when the mouse releases a dragger knot; changes gradient writing to repr, updates other draggers if needed
if (d == dragger)
d->fireDraggables (true);
if (!draggable) return;
switch (draggable->point_type) { // if we delete first or last stop, move the next/previous to the edge
case POINT_LG_BEGIN:
case POINT_RG_CENTER:
if (next) {
case POINT_LG_END:
case POINT_RG_R1:
case POINT_RG_R2:
if (prev) {
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
Called when a dragger knot is doubleclicked; opens gradient editor with the stop from the first draggable
sp_item_gradient_edit_stop (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
if (merging_focus ||
!(draggable->point_type == POINT_RG_FOCUS && this->isA(draggable->item, POINT_RG_CENTER, draggable->point_i, draggable->fill_or_stroke)))
sp_item_gradient_set_coords (draggable->item, draggable->point_type, draggable->point_i, this->point, draggable->fill_or_stroke, write_repr, scale_radial);
if ( (draggable->point_type == point_type) && (draggable->point_i == point_i) && (draggable->item == item) && (draggable->fill_or_stroke == fill_or_stroke) ) {
if ( (draggable->point_type == point_type) && (draggable->item == item) && (draggable->fill_or_stroke == fill_or_stroke) ) {
if (this == other)
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
this->knot->tip = g_strdup_printf (_("%s %d for: %s%s; drag with <b>Ctrl</b> to snap offset; click with <b>Ctrl+Alt</b> to delete stop"),
this->knot->tip = g_strdup_printf (_("%s for: %s%s; drag with <b>Ctrl</b> to snap angle, with <b>Ctrl+Alt</b> to preserve angle, with <b>Ctrl+Shift</b> to scale around center"),
this->knot->tip = g_strdup_printf (_("Radial gradient <b>center</b> and <b>focus</b>; drag with <b>Shift</b> to separate focus"));
this->knot->tip = g_strdup_printf (ngettext("Gradient point shared by <b>%d</b> gradient; drag with <b>Shift</b> to separate",
length),
length);
if (!draggables)
this->updateTip();
GrDragger::moveThisToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
sp_item_gradient_set_coords (da->item, da->point_type, da->point_i, this->point, da->fill_or_stroke, write_repr, false);
// FIXME: here we should also call this->updateDependencies(write_repr); to propagate updating, but how to prevent loops?
if (!server)
this->moveOtherToDraggable (draggable->item, POINT_LG_MID, i, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_MID1, i, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_MID2, i, draggable->fill_or_stroke, write_repr);
case POINT_LG_BEGIN:
this->moveOtherToDraggable (draggable->item, POINT_LG_END, -1, draggable->fill_or_stroke, write_repr);
case POINT_LG_END:
this->moveOtherToDraggable (draggable->item, POINT_LG_BEGIN, 0, draggable->fill_or_stroke, write_repr);
case POINT_LG_MID:
case POINT_RG_R2:
this->moveOtherToDraggable (draggable->item, POINT_RG_R1, -1, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
case POINT_RG_R1:
this->moveOtherToDraggable (draggable->item, POINT_RG_R2, -1, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
case POINT_RG_CENTER:
this->moveOtherToDraggable (draggable->item, POINT_RG_R1, -1, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_R2, -1, draggable->fill_or_stroke, write_repr);
this->moveOtherToDraggable (draggable->item, POINT_RG_FOCUS, -1, draggable->fill_or_stroke, write_repr);
case POINT_RG_FOCUS:
case POINT_RG_MID1:
this->moveOtherToDraggable (draggable->item, POINT_RG_MID2, draggable->point_i, draggable->fill_or_stroke, write_repr);
case POINT_RG_MID2:
this->moveOtherToDraggable (draggable->item, POINT_RG_MID1, draggable->point_i, draggable->fill_or_stroke, write_repr);
: point(p),
this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_midpoint_handler), this);
this->handler_id = g_signal_connect (G_OBJECT (this->knot), "moved", G_CALLBACK (gr_knot_moved_handler), this);
g_signal_connect (G_OBJECT (this->knot), "doubleclicked", G_CALLBACK (gr_knot_doubleclicked_handler), this);
g_signal_connect (G_OBJECT (this->knot), "ungrabbed", G_CALLBACK (gr_knot_ungrabbed_handler), this);
if (draggable)
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_moved_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_clicked_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_doubleclicked_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_grabbed_handler), this);
g_signal_handlers_disconnect_by_func(G_OBJECT(this->knot), (gpointer) G_CALLBACK (gr_knot_ungrabbed_handler), this);
return (dragger);
return NULL;
GrDragger::moveOtherToDraggable (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, bool write_repr)
while (selected) {
deselect_all();
setSelected (d, true, true);
setSelected (d, true, true);
setSelected (d, true, true);
if (add_to_selection) {
if (!dragger) return;
if (override) {
if (selected) {
deselect_all();
if (dragger) {
if (seldragger) {
if (rgba != GR_LINE_COLOR_FILL) // fill is the default, so don't set color for it to speed up redraw
Geom::Point p = sp_item_gradient_get_coords (draggable->item, draggable->point_type, draggable->point_i, draggable->fill_or_stroke);
// fixme: draggers should be added AFTER the last one: this way tabbing through them will be from begin to end.
if (dragger) {
GrDrag::grabKnot (SPItem *item, gint point_type, gint point_i, bool fill_or_stroke, gint x, gint y, guint32 etime)
if (dragger) {
while (selected) {
this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, true), sp_item_gradient_get_coords (item, POINT_LG_END, 0, true), GR_LINE_COLOR_FILL);
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, true), GR_LINE_COLOR_FILL);
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, true), GR_LINE_COLOR_FILL);
this->addLine (item, sp_item_gradient_get_coords (item, POINT_LG_BEGIN, 0, false), sp_item_gradient_get_coords (item, POINT_LG_END, 0, false), GR_LINE_COLOR_STROKE);
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R1, 0, false), GR_LINE_COLOR_STROKE);
this->addLine (item, center, sp_item_gradient_get_coords (item, POINT_RG_R2, 0, false), GR_LINE_COLOR_STROKE);
if (rect) {
bool did = false;
bool skip_radius_with_center = false;
skip_radius_with_center = true;
did = true;
did = true;
if (draggers)
setSelected (d);
if (draggers)
setSelected (d);
if (!selected) return;
struct StructStopInfo {
while (selected) {
case POINT_LG_MID:
case POINT_RG_MID1:
case POINT_RG_MID2:
bool present = false;
present = true;
if (!present)
case POINT_LG_BEGIN:
case POINT_LG_END:
case POINT_RG_CENTER:
case POINT_RG_R1:
case POINT_RG_R2:
if (stop) {
bool present = false;
present = true;
if (!present)
while (midstoplist) {
while (endstoplist) {
// cannot use vector->vector.stops.size() because the vector might be invalidated by deletion of a midstop
int len = 0;
case POINT_LG_BEGIN:
case POINT_LG_END:
case POINT_RG_CENTER:
if (newfirst) {
case POINT_RG_R1:
case POINT_RG_R2:
sp_repr_css_set_property(css, "fill", sp_repr_css_property(stopcss, "stop-color", "inkscape:unset"));
sp_repr_css_set_property(css, "stroke", sp_repr_css_property(stopcss, "stop-color", "inkscape:unset"));
sp_repr_css_set_property(css, "stroke-opacity", sp_repr_css_property(stopcss, "stop-opacity", "1"));
delete stopinfo;
if (document) {