Code

Implement object-snapping for clones (see bug #1511260)
[inkscape.git] / src / dyna-draw-context.cpp
index 56890b7677dc38e3b05254edf86d9ead21f53284..f6f59fe6b375d5fdab3247be36dec9573905f69d 100644 (file)
@@ -58,6 +58,7 @@
 #include "inkscape.h"
 #include "color.h"
 #include "splivarot.h"
+#include "sp-item-group.h"
 #include "sp-shape.h"
 #include "sp-path.h"
 #include "sp-text.h"
@@ -197,6 +198,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc)
     ddc->keep_selected = true;
 
     ddc->hatch_spacing = 0;
+    ddc->hatch_spacing_step = 0;
     new (&ddc->hatch_pointer_past) std::list<double>();
     new (&ddc->hatch_nearest_past) std::list<double>();
     ddc->hatch_last_nearest = NR::Point(0,0);
@@ -210,6 +212,7 @@ sp_dyna_draw_context_init(SPDynaDrawContext *ddc)
     ddc->trace_bg = false;
 
     ddc->is_dilating = false;
+    ddc->has_dilated = false;
 }
 
 static void
@@ -599,50 +602,46 @@ get_dilate_radius (SPDynaDrawContext *dc)
 double
 get_dilate_force (SPDynaDrawContext *dc)
 {
-    return 8 * dc->pressure/SP_EVENT_CONTEXT(dc)->desktop->current_zoom(); 
+    double force = 4 * dc->pressure/SP_EVENT_CONTEXT(dc)->desktop->current_zoom(); 
+    if (force > 3) {
+        force += 8 * (force - 3);
+    }
+    return force;
 }
 
 bool
-sp_ddc_dilate (SPDynaDrawContext *dc, NR::Point p, bool expand)
+sp_ddc_dilate_recursive (SPItem *item, NR::Point p, bool expand, double radius, double offset)
 {
-    Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(dc)->desktop);
-
-    if (selection->isEmpty()) {
-        return false;
-    }
-
     bool did = false;
-    double radius = get_dilate_radius(dc); 
-    double offset = get_dilate_force(dc); 
 
-    for (GSList *items = g_slist_copy((GSList *) selection->itemList());
-         items != NULL;
-         items = items->next) {
-
-        SPItem *item = (SPItem *) items->data;
+    if (SP_IS_GROUP(item)) {
+        for (SPObject *child = sp_object_first_child(SP_OBJECT(item)) ; child != NULL; child = SP_OBJECT_NEXT(child) ) {
+            if (SP_IS_ITEM(child)) {
+                if (sp_ddc_dilate_recursive (SP_ITEM(child), p, expand, radius, offset))
+                    did = true;
+            }
+        }
 
-        // only paths
-        if (!SP_IS_PATH(item))
-            continue;
+    } else if (SP_IS_PATH(item)) {
 
         SPCurve *curve = NULL;
         curve = sp_shape_get_curve(SP_SHAPE(item));
         if (curve == NULL)
-            continue;
+            return false;
 
         // skip those paths whose bboxes are entirely out of reach with our radius
         NR::Maybe<NR::Rect> bbox = item->getBounds(sp_item_i2doc_affine(item));
         if (bbox) {
             bbox->growBy(radius);
             if (!bbox->contains(p)) {
-                continue;
+                return false;
             }
         }
 
         Path *orig = Path_for_item(item, false);
         if (orig == NULL) {
             sp_curve_unref(curve);
-            continue;
+            return false;
         }
         Path *res = new Path;
         res->SetBackData(false);
@@ -669,10 +668,11 @@ sp_ddc_dilate (SPDynaDrawContext *dc, NR::Point p, bool expand)
         }
 
         bool did_this = false;
+        NR::Matrix i2doc(sp_item_i2doc_affine(item));
         if (theShape->MakeOffset(theRes, 
-                                  expand? offset : -offset,
-                                  join_straight, butt_straight,
-                                  true, p[NR::X], p[NR::Y], radius) == 0) // 0 means the shape was actually changed
+                                 expand? offset : -offset,
+                                 join_straight, butt_straight,
+                                 true, p[NR::X], p[NR::Y], radius, &i2doc) == 0) // 0 means the shape was actually changed
             did_this = true;
 
         // the rest only makes sense if we actually changed the path
@@ -715,6 +715,37 @@ sp_ddc_dilate (SPDynaDrawContext *dc, NR::Point p, bool expand)
     return did;
 }
 
+
+bool
+sp_ddc_dilate (SPDynaDrawContext *dc, NR::Point p, bool expand)
+{
+    Inkscape::Selection *selection = sp_desktop_selection(SP_EVENT_CONTEXT(dc)->desktop);
+
+    if (selection->isEmpty()) {
+        return false;
+    }
+
+    bool did = false;
+    double radius = get_dilate_radius(dc); 
+    double offset = get_dilate_force(dc); 
+    if (radius == 0 || offset == 0) {
+        return false;
+    }
+
+    for (GSList *items = g_slist_copy((GSList *) selection->itemList());
+         items != NULL;
+         items = items->next) {
+
+        SPItem *item = (SPItem *) items->data;
+
+        if (sp_ddc_dilate_recursive (item, p, expand, radius, offset))
+            did = true;
+
+    }
+
+    return did;
+}
+
 void
 sp_ddc_update_cursors (SPDynaDrawContext *dc)
 {
@@ -797,7 +828,12 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
 
                 ret = TRUE;
 
+                sp_canvas_force_full_redraw_after_interruptions(desktop->canvas, 3);
                 dc->is_drawing = true;
+                if (event->button.state & GDK_MOD1_MASK) {
+                    dc->is_dilating = true;
+                    dc->has_dilated = false;
+                }
             }
             break;
         case GDK_MOTION_NOTIFY:
@@ -805,6 +841,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
             NR::Point const motion_w(event->motion.x,
                                      event->motion.y);
             NR::Point motion_dt(desktop->w2d(motion_w));
+            sp_dyna_draw_extinput(dc, event);
 
             // draw the dilating cursor
             if (event->motion.state & GDK_MOD1_MASK) {
@@ -823,8 +860,8 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
                 } else {
                     dc->_message_context->setF(Inkscape::NORMAL_MESSAGE,
                                            event->motion.state & GDK_SHIFT_MASK?
-                                           _("<b>Thickening %d</b> selected paths; without <b>Shift</b> to thin") :
-                                           _("<b>Thinning %d</b> selected paths; with <b>Shift</b> to thicken"), num);
+                                           _("<b>Thickening %d</b> selected objects; without <b>Shift</b> to thin") :
+                                           _("<b>Thinning %d</b> selected objects; with <b>Shift</b> to thicken"), num);
                 }
 
             } else {
@@ -835,7 +872,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
             // dilating:
             if (dc->is_drawing && ( event->motion.state & GDK_BUTTON1_MASK ) && event->motion.state & GDK_MOD1_MASK) {  
                 sp_ddc_dilate (dc, desktop->dt2doc(motion_dt), event->motion.state & GDK_SHIFT_MASK? true : false);
-                dc->is_dilating = true;
+                dc->has_dilated = true;
                 // it's slow, so prevent clogging up with events
                 gobble_motion_events(GDK_BUTTON1_MASK);
                 return TRUE;
@@ -937,9 +974,6 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
                         // We are NOT attracted to the guide!
 
                         //g_print ("\nlast_nearest %g %g   nearest %g %g  pointer %g %g  pos %d %g\n", dc->last_nearest[NR::X], dc->last_nearest[NR::Y], nearest[NR::X], nearest[NR::Y], pointer[NR::X], pointer[NR::Y], position->piece, position->t);
-                        //g_print ("------nm %g  pm %g  ratio %g\n", nearest_moved, pointer_moved, ratio);
-                        //g_print ("------dist %g  spacing %g\n", dist, dc->spacing);
-                        //g_print ("acting up: %s dist %g \n", position_is_ok? (dc->hatch_escaped? "escaped" : "dist") : "livarot", dist/dc->hatch_spacing);
 
                         // Remember hatch_escaped so we don't get
                         // attracted again until the end of this stroke
@@ -965,12 +999,10 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
                             }
                             if (!isNaN(dot) && dot < -0.5) {// flip
                                 target = -target;
-                                //g_print ("FLIP\n");
                             }
 
                             // This is the track pointer that we will use instead of the real one
                             NR::Point new_pointer = nearest + target * hatch_unit_vector;
-                            //g_print ("NEW %g %g\n", new_point[NR::X], new_point[NR::Y]);
 
                             // some limited feedback: allow persistent pulling to slightly change
                             // the spacing
@@ -996,7 +1028,6 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
                     dc->_message_context->set(Inkscape::NORMAL_MESSAGE, _("<b>Drawing</b> a calligraphic stroke"));
                 }
 
-                sp_dyna_draw_extinput(dc, event);
                 if (!sp_dyna_draw_apply(dc, motion_dt)) {
                     ret = TRUE;
                     break;
@@ -1052,20 +1083,30 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
 
 
     case GDK_BUTTON_RELEASE:
+    {
+        NR::Point const motion_w(event->button.x, event->button.y);
+        NR::Point const motion_dt(desktop->w2d(motion_w));
+
         sp_canvas_item_ungrab(SP_CANVAS_ITEM(desktop->acetate), event->button.time);
+        sp_canvas_end_forced_full_redraws(desktop->canvas);
         dc->is_drawing = false;
 
         if ( dc->is_dilating && event->button.button == 1 ) {
+            if (!dc->has_dilated) {
+                // if we did not rub, do a light tap
+                dc->pressure = 0.03;
+                sp_ddc_dilate (dc, desktop->dt2doc(motion_dt), event->button.state & GDK_SHIFT_MASK? true : false);
+            }
             dc->is_dilating = false;
+            dc->has_dilated = false;
             sp_document_done(sp_desktop_document(SP_EVENT_CONTEXT(dc)->desktop), 
                          SP_VERB_CONTEXT_CALLIGRAPHIC,
                          (event->button.state & GDK_SHIFT_MASK ? _("Thicken paths") : _("Thin paths")));
             ret = TRUE;
+
         } else if ( dc->dragging && event->button.button == 1 ) {
             dc->dragging = FALSE;
 
-            NR::Point const motion_w(event->button.x, event->button.y);
-            NR::Point const motion_dt(desktop->w2d(motion_w));
             sp_dyna_draw_apply(dc, motion_dt);
 
             /* Remove all temporary line segments */
@@ -1096,10 +1137,19 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
             dc->hatch_item = NULL;
             dc->hatch_livarot_path = NULL;
 
+            if (dc->hatch_spacing != 0 && !dc->keep_selected) { 
+                // we do not select the newly drawn path, so increase spacing by step
+                if (dc->hatch_spacing_step == 0) {
+                    dc->hatch_spacing_step = dc->hatch_spacing;
+                }
+                dc->hatch_spacing += dc->hatch_spacing_step;
+            }
+
             dc->_message_context->clear();
             ret = TRUE;
         }
         break;
+    }
 
     case GDK_KEY_PRESS:
         switch (get_group0_keyval (&event->key)) {
@@ -1214,6 +1264,7 @@ sp_dyna_draw_context_root_handler(SPEventContext *event_context,
             case GDK_Control_R:
                 dc->_message_context->clear();
                 dc->hatch_spacing = 0;
+                dc->hatch_spacing_step = 0;
                 break;
             case GDK_Alt_L:
             case GDK_Alt_R:
@@ -1300,9 +1351,7 @@ set_to_accumulated(SPDynaDrawContext *dc, bool unionize)
         } else {
             if (dc->keep_selected) {
                 sp_desktop_selection(desktop)->set(dc->repr);
-            } else {
-                sp_desktop_selection(desktop)->clear();
-            }
+            } 
         }
 
     } else {