diff options
Diffstat (limited to 'src/document.cpp')
| -rw-r--r-- | src/document.cpp | 109 |
1 files changed, 75 insertions, 34 deletions
diff --git a/src/document.cpp b/src/document.cpp index 2ea969910..23d99d78c 100644 --- a/src/document.cpp +++ b/src/document.cpp @@ -1335,51 +1335,64 @@ SPItem *SPDocument::getItemFromListAtPointBottom(unsigned int dkey, SPGroup *gro } /** -Returns the topmost (in z-order) item from the descendants of group (recursively) which -is at the point p, or NULL if none. Honors into_groups on whether to recurse into -non-layer groups or not. Honors take_insensitive on whether to return insensitive -items. If upto != NULL, then if item upto is encountered (at any level), stops searching -upwards in z-order and returns what it has found so far (i.e. the found item is -guaranteed to be lower than upto). - */ -static SPItem *find_item_at_point(unsigned int dkey, SPGroup *group, Geom::Point const &p, gboolean into_groups, bool take_insensitive = false, SPItem *upto = NULL) +Turn the SVG DOM into a flat list of nodes that can be searched from top-down. +The list can be persisted, which improves "find at multiple points" speed. +Returns true if upto is reached. +*/ +static bool build_flat_item_list(std::deque<SPItem*> *nodes, unsigned int dkey, SPGroup *group, gboolean into_groups, bool take_insensitive = false, SPItem *upto = NULL) { - SPItem *seen = NULL; - SPItem *newseen = NULL; - Inkscape::Preferences *prefs = Inkscape::Preferences::get(); - gdouble delta = prefs->getDouble("/options/cursortolerance/value", 1.0); - + bool found_upto = false; for ( SPObject *o = group->firstChild() ; o ; o = o->getNext() ) { if (!SP_IS_ITEM(o)) { continue; } if (upto && SP_ITEM(o) == upto) { + found_upto = true; break; } if (SP_IS_GROUP(o) && (SP_GROUP(o)->effectiveLayerMode(dkey) == SPGroup::LAYER || into_groups)) { - // if nothing found yet, recurse into the group - newseen = find_item_at_point(dkey, SP_GROUP(o), p, into_groups, take_insensitive, upto); - if (newseen) { - seen = newseen; - newseen = NULL; - } - - if (item_is_in_group(upto, SP_GROUP(o))) { + found_upto = build_flat_item_list(nodes, dkey, SP_GROUP(o), into_groups, take_insensitive, upto); + if (found_upto) break; - } } else { SPItem *child = SP_ITEM(o); - Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey); - // seen remembers the last (topmost) of items pickable at this point - if (arenaitem && arenaitem->pick(p, delta, 1) != NULL - && (take_insensitive || child->isVisibleAndUnlocked(dkey))) { - seen = child; + if (take_insensitive || child->isVisibleAndUnlocked(dkey)) { + nodes->push_front(child); } } } + return found_upto; +} + +/** +Returns the topmost (in z-order) item from the descendants of group (recursively) which +is at the point p, or NULL if none. Honors into_groups on whether to recurse into +non-layer groups or not. Honors take_insensitive on whether to return insensitive +items. If upto != NULL, then if item upto is encountered (at any level), stops searching +upwards in z-order and returns what it has found so far (i.e. the found item is +guaranteed to be lower than upto). Requires a list of nodes built by +build_flat_item_list. + */ +static SPItem *find_item_at_point(std::deque<SPItem*> *nodes, unsigned int dkey, Geom::Point const &p) +{ + Inkscape::Preferences *prefs = Inkscape::Preferences::get(); + gdouble delta = prefs->getDouble("/options/cursortolerance/value", 1.0); + + SPItem *seen = NULL; + SPItem *child; + for (unsigned long i = 0; i < nodes->size(); ++i) { + child = nodes->at(i); + Inkscape::DrawingItem *arenaitem = child->get_arenaitem(dkey); + + if (arenaitem && arenaitem->pick(p, delta, 1) != NULL) { + seen = child; + break; + } + } + return seen; } @@ -1443,7 +1456,7 @@ std::vector<SPItem*> SPDocument::getItemsPartiallyInBox(unsigned int dkey, Geom: return find_items_in_area(x, SP_GROUP(this->root), dkey, box, overlaps); } -std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points) const +std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vector<Geom::Point> points, bool all_layers, size_t limit) const { std::vector<SPItem*> items; Inkscape::Preferences *prefs = Inkscape::Preferences::get(); @@ -1454,11 +1467,28 @@ std::vector<SPItem*> SPDocument::getItemsAtPoints(unsigned const key, std::vecto gdouble saved_delta = prefs->getDouble("/options/cursortolerance/value", 1.0); prefs->setDouble("/options/cursortolerance/value", 0.25); + // Cache a flattened SVG DOM to speed up selection. + std::deque<SPItem*> nodes; + build_flat_item_list(&nodes, key, SP_GROUP(this->root), true, false, NULL); + SPObject *current_layer = SP_ACTIVE_DESKTOP->currentLayer(); + SPDesktop *desktop = SP_ACTIVE_DESKTOP; + Inkscape::LayerModel *layer_model = NULL; + if(desktop){ + layer_model = desktop->layers; + } + size_t item_counter = 0; for(int i = points.size()-1;i>=0; i--) { - SPItem *item = getItemAtPoint(key, points[i], - false, NULL); + SPItem *item = find_item_at_point(&nodes, key, points[i]); if (item && items.end()==find(items.begin(),items.end(), item)) - items.push_back(item); + if(all_layers || (layer_model && layer_model->layerForObject(item) == current_layer)){ + items.push_back(item); + item_counter++; + //limit 0 = no limit + if(item_counter == limit){ + prefs->setDouble("/options/cursortolerance/value", saved_delta); + return items; + } + } } // and now we restore it back @@ -1472,7 +1502,11 @@ SPItem *SPDocument::getItemAtPoint( unsigned const key, Geom::Point const &p, { g_return_val_if_fail(this->priv != NULL, NULL); - return find_item_at_point(key, SP_GROUP(this->root), p, into_groups, false, upto); + // Build a flattened SVG DOM for find_item_at_point. + std::deque<SPItem*> nodes; + build_flat_item_list(&nodes, key, SP_GROUP(this->root), into_groups, false, upto); + + return find_item_at_point(&nodes, key, p); } SPItem *SPDocument::getGroupAtPoint(unsigned int key, Geom::Point const &p) const @@ -1482,7 +1516,6 @@ SPItem *SPDocument::getGroupAtPoint(unsigned int key, Geom::Point const &p) cons return find_group_at_point(key, SP_GROUP(this->root), p); } - // Resource management bool SPDocument::addResource(gchar const *key, SPObject *object) @@ -1501,7 +1534,15 @@ bool SPDocument::addResource(gchar const *key, SPObject *object) g_hash_table_insert(priv->resources, (gpointer) key, rlist); GQuark q = g_quark_from_string(key); - priv->resources_changed_signals[q].emit(); + + /*in general, do not send signal if the object has no id (yet), + it means the object is not completely built. + (happens when pasting swatches across documents, cf bug 1495106) + [this check should be more generally presend on emit() calls since + the backtrace is unusable with crashed from this cause] + */ + if(object->getId() || dynamic_cast<SPGroup*>(object) ) + priv->resources_changed_signals[q].emit(); result = true; } |
