From 0278e700a0205d07eceab62c749786209fc5888d Mon Sep 17 00:00:00 2001 From: Quincy Morgan Date: Wed, 13 May 2020 15:42:28 -0400 Subject: [PATCH] Convert feature copying functionality from a standalone behavior to an operation and add to edit menu (re: #2508) Show flash feedback message when copying features with keyboard shortcut Disallow copying of untagged vertices --- data/core.yaml | 8 ++ dist/locales/en.json | 11 ++ modules/behavior/index.js | 1 - modules/modes/select.js | 2 - modules/{behavior => operations}/copy.js | 124 ++++++++++++-------- modules/operations/index.js | 1 + svg/iD-sprite/operations/operation-copy.svg | 2 +- 7 files changed, 96 insertions(+), 53 deletions(-) rename modules/{behavior => operations}/copy.js (61%) diff --git a/data/core.yaml b/data/core.yaml index ce1b23974..1ed815305 100644 --- a/data/core.yaml +++ b/data/core.yaml @@ -80,6 +80,14 @@ en: annotation: Changed the role of a relation member. change_tags: annotation: Changed tags. + copy: + title: Copy + description: + single: Make this feature pasteable. + multiple: Make these features pasteable. + annotation: + single: Copied a feature. + multiple: "Copied {n} features." circularize: title: Circularize description: diff --git a/dist/locales/en.json b/dist/locales/en.json index bf0dd4c93..04722c333 100644 --- a/dist/locales/en.json +++ b/dist/locales/en.json @@ -103,6 +103,17 @@ "change_tags": { "annotation": "Changed tags." }, + "copy": { + "title": "Copy", + "description": { + "single": "Make this feature pasteable.", + "multiple": "Make these features pasteable." + }, + "annotation": { + "single": "Copied a feature.", + "multiple": "Copied {n} features." + } + }, "circularize": { "title": "Circularize", "description": { diff --git a/modules/behavior/index.js b/modules/behavior/index.js index 054dbb6cf..19b0a8da0 100644 --- a/modules/behavior/index.js +++ b/modules/behavior/index.js @@ -1,6 +1,5 @@ export { behaviorAddWay } from './add_way'; export { behaviorBreathe } from './breathe'; -export { behaviorCopy } from './copy'; export { behaviorDrag } from './drag'; export { behaviorDrawWay } from './draw_way'; export { behaviorDraw } from './draw'; diff --git a/modules/modes/select.js b/modules/modes/select.js index 358f7db08..f5191e080 100644 --- a/modules/modes/select.js +++ b/modules/modes/select.js @@ -6,7 +6,6 @@ import { actionAddMidpoint } from '../actions/add_midpoint'; import { actionDeleteRelation } from '../actions/delete_relation'; import { behaviorBreathe } from '../behavior/breathe'; -import { behaviorCopy } from '../behavior/copy'; import { behaviorHover } from '../behavior/hover'; import { behaviorLasso } from '../behavior/lasso'; import { behaviorPaste } from '../behavior/paste'; @@ -38,7 +37,6 @@ export function modeSelect(context, selectedIDs) { var keybinding = utilKeybinding('select'); var breatheBehavior = behaviorBreathe(context); var behaviors = [ - behaviorCopy(context), behaviorPaste(context), breatheBehavior, behaviorHover(context), diff --git a/modules/behavior/copy.js b/modules/operations/copy.js similarity index 61% rename from modules/behavior/copy.js rename to modules/operations/copy.js index 05da18630..95372463d 100644 --- a/modules/behavior/copy.js +++ b/modules/operations/copy.js @@ -1,10 +1,57 @@ import { event as d3_event } from 'd3-selection'; +import { t } from '../core/localizer'; +import { behaviorOperation } from '../behavior/operation'; import { uiCmd } from '../ui/cmd'; import { utilArrayGroupBy } from '../util'; +export function operationCopy(selectedIDs, context) { + + function getFilteredIdsToCopy() { + return selectedIDs.filter(function(selectedID) { + var entity = context.graph().hasEntity(selectedID); + // don't copy untagged vertices separately from ways + return entity.hasInterestingTags() || entity.geometry(context.graph()) !== 'vertex'; + }); + } + + var operation = function() { + + if (!getSelectionText()) { + d3_event.preventDefault(); + } + + var graph = context.graph(); + var selected = groupEntities(getFilteredIdsToCopy(), graph); + var canCopy = []; + var skip = {}; + var entity; + var i; + + for (i = 0; i < selected.relation.length; i++) { + entity = selected.relation[i]; + if (!skip[entity.id] && entity.isComplete(graph)) { + 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.copyIDs(canCopy); + }; -export function behaviorCopy(context) { function groupEntities(ids, graph) { var entities = ids.map(function (id) { return graph.entity(id); }); @@ -45,55 +92,34 @@ export function behaviorCopy(context) { } - function doCopy() { - // prevent copy during low zoom selection - if (!context.map().withinEditableZoom()) return; - - if (!getSelectionText()) { - d3_event.preventDefault(); - } - - var graph = context.graph(); - var selected = groupEntities(context.selectedIDs(), graph); - var canCopy = []; - var skip = {}; - var entity; - var i; - - for (i = 0; i < selected.relation.length; i++) { - entity = selected.relation[i]; - if (!skip[entity.id] && entity.isComplete(graph)) { - 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.copyIDs(canCopy); - } - - - function behavior() { - context.keybinding().on(uiCmd('⌘C'), doCopy); - return behavior; - } - - behavior.off = function() { - context.keybinding().off(uiCmd('⌘C')); + operation.available = function() { + return getFilteredIdsToCopy().length > 0; }; - return behavior; + operation.disabled = function() { + return false; + }; + + + operation.tooltip = function() { + return selectedIDs.length === 1 ? + t('operations.copy.description.single') : + t('operations.copy.description.multiple'); + }; + + + operation.annotation = function() { + return selectedIDs.length === 1 ? + t('operations.copy.annotation.single') : + t('operations.copy.annotation.multiple', { n: selectedIDs.length }); + }; + + + operation.id = 'copy'; + operation.keys = [uiCmd('⌘C')]; + operation.title = t('operations.copy.title'); + operation.behavior = behaviorOperation(context).which(operation); + + return operation; } diff --git a/modules/operations/index.js b/modules/operations/index.js index 77be52df1..6584fa294 100644 --- a/modules/operations/index.js +++ b/modules/operations/index.js @@ -1,5 +1,6 @@ export { operationCircularize } from './circularize'; export { operationContinue } from './continue'; +export { operationCopy } from './copy'; export { operationDelete } from './delete'; export { operationDisconnect } from './disconnect'; export { operationDowngrade } from './downgrade'; diff --git a/svg/iD-sprite/operations/operation-copy.svg b/svg/iD-sprite/operations/operation-copy.svg index 30ebe5309..92e0d9d90 100644 --- a/svg/iD-sprite/operations/operation-copy.svg +++ b/svg/iD-sprite/operations/operation-copy.svg @@ -1,6 +1,6 @@ - +