diff --git a/modules/behavior/operation.js b/modules/behavior/operation.js index a3456c674..c4aa722a0 100644 --- a/modules/behavior/operation.js +++ b/modules/behavior/operation.js @@ -30,6 +30,7 @@ export function behaviorOperation(context) { .text(_operation.annotation() || _operation.title); flash(); + if (_operation.point) _operation.point(null); _operation(); } } diff --git a/modules/behavior/paste.js b/modules/behavior/paste.js index f76cffee2..a31a43d36 100644 --- a/modules/behavior/paste.js +++ b/modules/behavior/paste.js @@ -54,8 +54,8 @@ export function behaviorPaste(context) { } // Put pasted objects where mouse pointer is.. - var center = projection(extent.center()); - var delta = geoVecSubtract(mouse, center); + var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || projection(extent.center()); + var delta = geoVecSubtract(mouse, copyPoint); context.perform(actionMove(newIDs, delta, projection)); context.enter(modeMove(context, newIDs, baseGraph)); diff --git a/modules/core/context.js b/modules/core/context.js index 0cba035db..13d9d6b52 100644 --- a/modules/core/context.js +++ b/modules/core/context.js @@ -282,6 +282,13 @@ export function coreContext() { return context; }; + let _copyLonLat; + context.copyLonLat = function(val) { + if (!arguments.length) return _copyLonLat; + _copyLonLat = val; + return context; + }; + /* Background */ let _background; diff --git a/modules/operations/copy.js b/modules/operations/copy.js index bd81c8943..a58724169 100644 --- a/modules/operations/copy.js +++ b/modules/operations/copy.js @@ -53,6 +53,14 @@ export function operationCopy(context, selectedIDs) { } context.copyIDs(canCopy); + if (_point && + (canCopy.length !== 1 || graph.entity(canCopy[0]).type !== 'node')) { + // store the anchor coordinates if copying more than a single node + context.copyLonLat(context.projection.invert(_point)); + } else { + context.copyLonLat(null); + } + }; @@ -127,6 +135,13 @@ export function operationCopy(context, selectedIDs) { }; + var _point; + operation.point = function(val) { + _point = val; + return operation; + }; + + operation.id = 'copy'; operation.keys = [uiCmd('⌘C')]; operation.title = t('operations.copy.title'); diff --git a/modules/operations/paste.js b/modules/operations/paste.js index 8b36b3f61..297465346 100644 --- a/modules/operations/paste.js +++ b/modules/operations/paste.js @@ -10,11 +10,11 @@ import { utilDisplayLabel } from '../util/util'; // see also `behaviorPaste` export function operationPaste(context) { - var _point; + var _pastePoint; var operation = function() { - if (!_point) return; + if (!_pastePoint) return; var oldIDs = context.copyIDs(); if (!oldIDs.length) return; @@ -48,16 +48,19 @@ export function operationPaste(context) { } } - // Put pasted objects where mouse pointer is.. - var center = projection(extent.center()); - var delta = geoVecSubtract(_point, center); + // Use the location of the copy operation to offset the paste location, + // or else use the center of the pasted extent + var copyPoint = (context.copyLonLat() && projection(context.copyLonLat())) || + projection(extent.center()); + var delta = geoVecSubtract(_pastePoint, copyPoint); + // Move the pasted objects to be anchored at the paste location context.replace(actionMove(newIDs, delta, projection), operation.annotation()); context.enter(modeSelect(context, newIDs)); }; operation.point = function(val) { - _point = val; + _pastePoint = val; return operation; };