Add Copy/Paste behaviors, context copybuffer

This commit is contained in:
Bryan Housel
2015-01-06 22:22:04 -05:00
parent 90147b23db
commit 1603b638f6
8 changed files with 179 additions and 3 deletions
+2
View File
@@ -171,6 +171,7 @@
<script src='js/id/behavior.js'></script>
<script src='js/id/behavior/add_way.js'></script>
<script src='js/id/behavior/copy.js'></script>
<script src='js/id/behavior/drag.js'></script>
<script src='js/id/behavior/draw.js'></script>
<script src='js/id/behavior/draw_way.js'></script>
@@ -178,6 +179,7 @@
<script src='js/id/behavior/hash.js'></script>
<script src='js/id/behavior/hover.js'></script>
<script src='js/id/behavior/lasso.js'></script>
<script src='js/id/behavior/paste.js'></script>
<script src='js/id/behavior/select.js'></script>
<script src='js/id/behavior/tail.js'></script>
+11 -3
View File
@@ -1,11 +1,19 @@
iD.actions.CopyEntity = function(entity, deep) {
return function(graph) {
var newEntities = entity.copy(deep, graph);
var newEntities = [];
for (var i = 0, imax = newEntities.length; i !== imax; i++) {
var action = function(graph) {
newEntities = entity.copy(deep, graph);
for (var i = 0; i < newEntities.length; i++) {
graph = graph.replace(newEntities[i]);
}
return graph;
};
action.newEntities = function() {
return newEntities;
};
return action;
};
+78
View File
@@ -0,0 +1,78 @@
iD.behavior.Copy = function(context) {
var keybinding = d3.keybinding('copy');
function groupEntities(ids, graph) {
var entities = ids.map(function (id) { return graph.entity(id); });
return _.extend({relation: [], way: [], node: []},
_.groupBy(entities, function(entity) { return entity.type; }));
}
function getDescendants(id, graph, descendants) {
var entity = graph.entity(id),
i, children;
descendants = descendants || {};
if (entity.type === 'relation') {
children = _.pluck(entity.members, 'id');
} else if (entity.type === 'way') {
children = entity.nodes;
} else {
children = [];
}
for (i = 0; i < children.length; i++) {
if (!descendants[children[i]]) {
descendants[children[i]] = true;
descendants = getDescendants(children[i], graph, descendants);
}
}
return descendants;
}
function doCopy() {
d3.event.preventDefault();
var graph = context.graph(),
selected = groupEntities(context.selectedIDs(), graph),
canCopy = [],
skip = {},
i, entity;
for (i = 0; i < selected.relation.length; i++) {
entity = selected.relation[i];
if (!skip[entity.id] && entity.isComplete()) {
canCopy.push(entity.id);
skip = getDescendants(entity.id, graph, skip);
}
}
for (i = 0; i < selected.way.length; i++) {
entity = selected.way[i];
if (!skip[entity.id]) {
canCopy.push(entity.id);
skip = getDescendants(entity.id, graph, skip);
}
}
for (i = 0; i < selected.node.length; i++) {
entity = selected.node[i];
if (!skip[entity.id]) {
canCopy.push(entity.id);
}
}
context.copiedIDs(canCopy);
}
function copy() {
keybinding.on(iD.ui.cmd('⌘C'), doCopy);
d3.select(document).call(keybinding);
return copy;
}
copy.off = function() {
d3.select(document).call(keybinding.off);
};
return copy;
};
+75
View File
@@ -0,0 +1,75 @@
iD.behavior.Paste = function(context) {
var keybinding = d3.keybinding('paste');
function omitTag(v, k) {
return (
k === 'phone' ||
k === 'fax' ||
k === 'email' ||
k === 'website' ||
k === 'url' ||
k === 'note' ||
k === 'description' ||
k.indexOf('name') !== -1 ||
k.indexOf('wiki') === 0 ||
k.indexOf('addr:') === 0 ||
k.indexOf('contact:') === 0
);
}
function doPaste() {
d3.event.preventDefault();
var mouse = context.mouse(),
projection = context.projection,
viewport = iD.geo.Extent(projection.clipExtent()).polygon();
if (!iD.geo.pointInPolygon(mouse, viewport)) return;
var graph = context.graph(),
extent = iD.geo.Extent(),
oldIDs = context.copiedIDs(),
newIDs = [],
i, j;
for (i = 0; i < oldIDs.length; i++) {
var oldEntity = graph.entity(oldIDs[i]),
action = iD.actions.CopyEntity(oldEntity, true),
newEntities;
extent._extend(oldEntity.extent(graph));
context.perform(action);
// First element in `newEntities` contains the copied Entity,
// Subsequent array elements contain any descendants..
newEntities = action.newEntities();
newIDs.push(newEntities[0].id);
for (j = 0; j < newEntities.length; j++) {
var newEntity = newEntities[j],
tags = _.omit(newEntity.tags, omitTag);
context.perform(iD.actions.ChangeTags(newEntity.id, tags));
}
}
// Put pasted objects where mouse pointer is..
var center = projection(extent.center()),
delta = [ mouse[0] - center[0], mouse[1] - center[1] ];
context.perform(iD.actions.Move(newIDs, delta, projection));
context.enter(iD.modes.Move(context, newIDs));
}
function paste() {
keybinding.on(iD.ui.cmd('⌘V'), doPaste);
d3.select(document).call(keybinding);
return paste;
}
paste.off = function() {
d3.select(document).call(keybinding.off);
};
return paste;
};
+8
View File
@@ -204,6 +204,14 @@ window.iD = function () {
context.surface().call(behavior.off);
};
/* Copy/Paste */
var copiedIDs = [];
context.copiedIDs = function(_) {
if (!arguments.length) return copiedIDs;
copiedIDs = _;
return context;
};
/* Projection */
context.projection = iD.geo.RawMercator();
+1
View File
@@ -7,6 +7,7 @@ iD.modes.Browse = function(context) {
}, sidebar;
var behaviors = [
iD.behavior.Paste(context),
iD.behavior.Hover(context)
.on('hover', context.ui().sidebar.hover),
iD.behavior.Select(context),
+2
View File
@@ -7,6 +7,8 @@ iD.modes.Select = function(context, selectedIDs) {
var keybinding = d3.keybinding('select'),
timeout = null,
behaviors = [
iD.behavior.Copy(context),
iD.behavior.Paste(context),
iD.behavior.Hover(context),
iD.behavior.Select(context),
iD.behavior.Lasso(context),
+2
View File
@@ -150,6 +150,7 @@
<script src='../js/id/behavior.js'></script>
<script src='../js/id/behavior/add_way.js'></script>
<script src='../js/id/behavior/copy.js'></script>
<script src='../js/id/behavior/drag.js'></script>
<script src='../js/id/behavior/draw.js'></script>
<script src='../js/id/behavior/draw_way.js'></script>
@@ -157,6 +158,7 @@
<script src='../js/id/behavior/hash.js'></script>
<script src='../js/id/behavior/hover.js'></script>
<script src='../js/id/behavior/lasso.js'></script>
<script src='../js/id/behavior/paste.js'></script>
<script src='../js/id/behavior/select.js'></script>
<script src='../js/id/behavior/tail.js'></script>