Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for "duplicate" function #2757

Merged
merged 4 commits into from
Jan 8, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions documentation/docs/help/en/Node selected.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@ If the node has a direction tag with a degree value, rotate the node by dragging

Copy the node to the internal copy and paste buffer.

### ![Duplicate](../images/content_duplicate_light.png) Duplicate

Create a copy of the selected node in the same location. This does not utilize the copy and paste buffer.

### ![Cut](../images/ic_menu_cut_holo_light.png) Cut

Move the node to the internal copy and paste buffer removing it from the data.
Expand Down
12 changes: 12 additions & 0 deletions documentation/docs/help/en/Relation selected.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,18 @@ Start Multi-Select mode with all members of the current relation selected. The r

If the relation is a multi-polygon, rotate it around its centroid by dragging the display roughly in a circle. The centroid position is marked with a cross.

### ![Copy](../images/ic_menu_copy_holo_light.png) Copy

Copy the selected relation to the internal copy and paste buffer. Only available for multi-polygon relations.

### ![Duplicate](../images/content_duplicate_light.png) Duplicate

Create a copy of the selected relation in the same location. Only available for multi-polygon relations. This does not utilize the copy and paste buffer.

### Shallow duplicate

Create a copy of the selected relation with the same member elements. This does not utilize the copy and paste buffer.

### ![Delete](../images/tag_menu_delete.png) Delete

Remove the object from the data.
Expand Down
8 changes: 8 additions & 0 deletions documentation/docs/help/en/Way selected.md
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ Go to the end or start of the selected way.

Copy the way to the internal copy and paste buffer.

### ![Duplicate](../images/content_duplicate_light.png) Duplicate

Create a copy of the selected way in the same location. This does not utilize the copy and paste buffer.

### Shallow duplicate

Create a copy of the selected way with the same way nodes. This does not utilize the copy and paste buffer.

### ![Cut](../images/ic_menu_cut_holo_light.png) Cut

Move the way to the internal copy and paste buffer removing it from the data.
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions src/androidTest/java/de/blau/android/easyedit/WayTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -175,8 +175,8 @@ public void splitAndMergeWay() {
TestUtils.clickAtCoordinates(device, map, splitNode.getLon(), splitNode.getLat());
TestUtils.sleep(2000);
assertTrue(TestUtils.textGone(device, context.getString(R.string.actionmode_wayselect), 5000));
TestUtils.clickAtCoordinates(device, map, 8.3893820, 47.3895626, true);
assertTrue(TestUtils.clickText(device, false, "↗ Path", false, false));
// TestUtils.clickAtCoordinates(device, map, 8.3893820, 47.3895626, true);
// assertTrue(TestUtils.clickText(device, false, "↗ Path", false, false));
assertTrue(TestUtils.findText(device, false, context.getString(R.string.actionmode_wayselect)));
way = App.getLogic().getSelectedWay();
assertNotNull(way);
Expand Down
2 changes: 1 addition & 1 deletion src/main/assets/help/en/Multiselect.html
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ <h3><img src="../images/tag_menu_merge.png" alt="Merge" /> Merge ways</h3>
<h3>Extract segment</h3>
<p>If you have selected exactly two nodes on the same way, you can extract the segment of the way between the two nodes. If the way is closed the segment extracted will between the first and 2nd node selected in the winding direction (clockwise or counterclockwise) of the way.</p>
<p>If the way has <em>highway</em> or <em>waterway</em> tagging a number of shortcuts will be displayed, for example to change a <em>footway</em> in to <em>steps</em>.</p>
<h3>Add node at intersection</h3>
<h3>Add node at intersectionn</h3>
<p>If two or more ways are selected and they intersect without a common node, a new node will be added at the first intersection found.</p>
<h3>Create circle</h3>
<p>Creates a circle if at least three nodes are selected. If you select the nodes in clockwise/counterclockwise direction the resulting way will turn clockwise/counterclockwise. Nodes are spaced roughly 2 meters apart for larger circles. If additional nodes are too near to the original ones they will not be added. Note that if the selected nodes do not form a valid polygon, the behaviour of the function is undefined.</p>
Expand Down
2 changes: 2 additions & 0 deletions src/main/assets/help/en/Node selected.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ <h3><img src="../images/ic_menu_rotate.png" alt="Rotate" /> Rotate</h3>
<p>If the node has a direction tag with a degree value, rotate the node by dragging the display roughly in a circle.</p>
<h3><img src="../images/ic_menu_copy_holo_light.png" alt="Copy" /> Copy</h3>
<p>Copy the node to the internal copy and paste buffer.</p>
<h3><img src="../images/content_duplicate_light.png" alt="Duplicate" /> Duplicate</h3>
<p>Create a copy of the selected node in the same location. This does not utilize the copy and paste buffer.</p>
<h3><img src="../images/ic_menu_cut_holo_light.png" alt="Cut" /> Cut</h3>
<p>Move the node to the internal copy and paste buffer removing it from the data.</p>
<h3>Paste tags</h3>
Expand Down
6 changes: 6 additions & 0 deletions src/main/assets/help/en/Relation selected.html
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,12 @@ <h3><img src="../images/relation_members.png" alt="RelationMembers" /> Select re
<p>Start Multi-Select mode with all members of the current relation selected. The relation itself will be deselected.</p>
<h3><img src="../images/ic_menu_rotate.png" alt="Rotate" /> Rotate</h3>
<p>If the relation is a multi-polygon, rotate it around its centroid by dragging the display roughly in a circle. The centroid position is marked with a cross.</p>
<h3><img src="../images/ic_menu_copy_holo_light.png" alt="Copy" /> Copy</h3>
<p>Copy the selected relation to the internal copy and paste buffer. Only available for multi-polygon relations.</p>
<h3><img src="../images/content_duplicate_light.png" alt="Duplicate" /> Duplicate</h3>
<p>Create a copy of the selected relation in the same location. Only available for multi-polygon relations. This does not utilize the copy and paste buffer.</p>
<h3>Shallow duplicate</h3>
<p>Create a copy of the selected relation with the same member elements. This does not utilize the copy and paste buffer.</p>
<h3><img src="../images/tag_menu_delete.png" alt="Delete" /> Delete</h3>
<p>Remove the object from the data.</p>
<h3>Paste tags</h3>
Expand Down
4 changes: 4 additions & 0 deletions src/main/assets/help/en/Way selected.html
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ <h3>Start/End of Way</h3>
<p>Go to the end or start of the selected way.</p>
<h3><img src="../images/ic_menu_copy_holo_light.png" alt="Copy" /> Copy</h3>
<p>Copy the way to the internal copy and paste buffer.</p>
<h3><img src="../images/content_duplicate_light.png" alt="Duplicate" /> Duplicate</h3>
<p>Create a copy of the selected way in the same location. This does not utilize the copy and paste buffer.</p>
<h3>Shallow duplicate</h3>
<p>Create a copy of the selected way with the same way nodes. This does not utilize the copy and paste buffer.</p>
<h3><img src="../images/ic_menu_cut_holo_light.png" alt="Cut" /> Cut</h3>
<p>Move the way to the internal copy and paste buffer removing it from the data.</p>
<h3>Paste tags</h3>
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
27 changes: 26 additions & 1 deletion src/main/java/de/blau/android/Logic.java
Original file line number Diff line number Diff line change
Expand Up @@ -5915,7 +5915,7 @@ private int[] calcCentroid(@NonNull List<OsmElement> elements) {
* @param activity the activity we were called from
* @param x screen x to position the object at
* @param y screen y to position the object at
* @return the pasted object or null if the clipboard was empty
* @return the pasted objects or null if the clipboard was empty
*/
@Nullable
public List<OsmElement> pasteFromClipboard(@Nullable Activity activity, float x, float y) {
Expand All @@ -5925,6 +5925,31 @@ public List<OsmElement> pasteFromClipboard(@Nullable Activity activity, float x,
return getDelegator().pasteFromClipboard(lat, lon);
}

/**
* Duplicate a list of elements
*
* If the the duplicated objects have a name tag, a compositie name will be generated
*
* @param activity the activity we were called from
* @param elements the List of OsmElement
* @param deep duplicate child elements if true
* @return the duplicated objects
*/
@Nullable
public List<OsmElement> duplicate(@Nullable Activity activity, @NonNull List<OsmElement> elements, boolean deep) {
createCheckpoint(activity, R.string.undo_action_duplicate);
List<OsmElement> duplicated = getDelegator().duplicate(elements, deep);
for (OsmElement d : duplicated) {
String nameTag = d.getTagWithKey(Tags.KEY_NAME);
if (nameTag != null && activity != null) {
SortedMap<String, String> tags = new TreeMap<>(d.getTags());
tags.put(Tags.KEY_NAME, activity.getString(R.string.duplicated_name_template, nameTag));
setTags(activity, d, tags, false);
}
}
return duplicated;
}

/**
* Check if the clipboard is empty
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,24 +95,26 @@ public abstract class ElementSelectionActionModeCallback extends EasyEditActionM
private static final int MENUITEM_HISTORY_WEB = 4;
private static final int MENUITEM_HISTORY = 5;
static final int MENUITEM_COPY = 6;
static final int MENUITEM_CUT = 7;
private static final int MENUITEM_PASTE_TAGS = 8;
private static final int MENUITEM_CREATE_RELATION = 9;
private static final int MENUITEM_ADD_RELATION_MEMBERS = 10;
private static final int MENUITEM_EXTEND_SELECTION = 11;
private static final int MENUITEM_ELEMENT_INFO = 12;
static final int MENUITEM_DUPLICATE = 7;
static final int MENUITEM_SHALLOW_DUPLICATE = 8;
static final int MENUITEM_CUT = 9;
private static final int MENUITEM_PASTE_TAGS = 10;
private static final int MENUITEM_CREATE_RELATION = 11;
private static final int MENUITEM_ADD_RELATION_MEMBERS = 12;
private static final int MENUITEM_EXTEND_SELECTION = 13;
private static final int MENUITEM_ELEMENT_INFO = 14;
protected static final int LAST_REGULAR_MENUITEM = MENUITEM_ELEMENT_INFO;

static final int MENUITEM_UPLOAD = 31;
protected static final int MENUITEM_SHARE_POSITION = 32;
private static final int MENUITEM_TAG_LAST = 33;
static final int MENUITEM_ZOOM_TO_SELECTION = 34;
static final int MENUITEM_SEARCH_OBJECTS = 35;
private static final int MENUITEM_REPLACE_GEOMETRY = 36;
private static final int MENUITEM_CALIBRATE_BAROMETER = 37;
static final int MENUITEM_PREFERENCES = 38;
static final int MENUITEM_JS_CONSOLE = 39;
static final int MENUITEM_ADD_TO_TODO = 40;
static final int MENUITEM_UPLOAD = 40;
protected static final int MENUITEM_SHARE_POSITION = 41;
private static final int MENUITEM_TAG_LAST = 42;
static final int MENUITEM_ZOOM_TO_SELECTION = 43;
static final int MENUITEM_SEARCH_OBJECTS = 44;
private static final int MENUITEM_REPLACE_GEOMETRY = 45;
private static final int MENUITEM_CALIBRATE_BAROMETER = 46;
static final int MENUITEM_PREFERENCES = 47;
static final int MENUITEM_JS_CONSOLE = 48;
static final int MENUITEM_ADD_TO_TODO = 49;

private static final int MENUITEM_TODO_CLOSE_AND_NEXT = 70;
private static final int MENUITEM_TODO_SKIP_AND_NEXT = 71;
Expand Down Expand Up @@ -178,10 +180,17 @@ public boolean onCreateActionMode(ActionMode mode, Menu menu) {

menu.add(Menu.NONE, MENUITEM_DELETE, Menu.CATEGORY_SYSTEM, R.string.delete).setIcon(ThemeUtils.getResIdFromAttribute(main, R.attr.menu_delete));
final boolean isRelation = element instanceof Relation;
if (!isRelation) {
if (!isRelation || element.hasTag(Tags.KEY_TYPE, Tags.VALUE_MULTIPOLYGON)) {
menu.add(Menu.NONE, MENUITEM_COPY, Menu.CATEGORY_SECONDARY, R.string.menu_copy).setIcon(ThemeUtils.getResIdFromAttribute(main, R.attr.menu_copy));
menu.add(Menu.NONE, MENUITEM_DUPLICATE, Menu.CATEGORY_SECONDARY, R.string.menu_duplicate)
.setIcon(ThemeUtils.getResIdFromAttribute(main, R.attr.menu_duplicate));
}
if (!isRelation) {
menu.add(Menu.NONE, MENUITEM_CUT, Menu.CATEGORY_SECONDARY, R.string.menu_cut).setIcon(ThemeUtils.getResIdFromAttribute(main, R.attr.menu_cut));
}
if (!(element instanceof Node)) {
menu.add(Menu.NONE, MENUITEM_SHALLOW_DUPLICATE, Menu.CATEGORY_SECONDARY, R.string.menu_shallow_duplicate);
}
pasteItem = menu.add(Menu.NONE, MENUITEM_PASTE_TAGS, Menu.CATEGORY_SECONDARY, R.string.menu_paste_tags);

menu.add(GROUP_BASE, MENUITEM_EXTEND_SELECTION, Menu.CATEGORY_SYSTEM, R.string.menu_extend_selection)
Expand Down Expand Up @@ -312,7 +321,8 @@ public static boolean setItemVisibility(boolean condition, @NonNull MenuItem ite
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
super.onActionItemClicked(mode, item);
final TaskStorage taskStorage = App.getTaskStorage();
switch (item.getItemId()) {
final int itemId = item.getItemId();
switch (itemId) {
case MENUITEM_TAG:
main.performTagEdit(element, null, false, false);
break;
Expand All @@ -339,6 +349,13 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
logic.cutToClipboard(main, element);
mode.finish();
break;
case MENUITEM_DUPLICATE:
case MENUITEM_SHALLOW_DUPLICATE:
List<OsmElement> result = logic.duplicate(main, Util.wrapInList(element), itemId == MENUITEM_DUPLICATE);
mode.finish();
App.getLogic().setSelection(result);
manager.editElements();
break;
case MENUITEM_PASTE_TAGS:
main.performTagEdit(element, null, new HashMap<>(App.getTagClipboard(main).paste()), false);
break;
Expand Down Expand Up @@ -412,7 +429,7 @@ public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
case MENUITEM_TODO_CLOSE_AND_NEXT:
case MENUITEM_TODO_SKIP_AND_NEXT:
final List<Todo> todos = taskStorage.getTodosForElement(element);
State newState = item.getItemId() == MENUITEM_TODO_CLOSE_AND_NEXT ? State.CLOSED : State.SKIPPED;
State newState = itemId == MENUITEM_TODO_CLOSE_AND_NEXT ? State.CLOSED : State.SKIPPED;
Set<StringWithDescription> listNames = new HashSet<>();
for (int i = 0; i < todos.size(); i++) {
listNames.add(todos.get(i).getListName(main));
Expand Down
13 changes: 11 additions & 2 deletions src/main/java/de/blau/android/osm/ClipboardStorage.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.blau.android.osm;

import static de.blau.android.contract.Constants.LOG_TAG_LEN;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
Expand All @@ -9,7 +11,9 @@
import de.blau.android.exception.StorageException;

public class ClipboardStorage implements Serializable {
static final String DEBUG_TAG = ClipboardStorage.class.getSimpleName().substring(0, Math.min(23, ClipboardStorage.class.getSimpleName().length()));

private static final int TAG_LEN = Math.min(LOG_TAG_LEN, ClipboardStorage.class.getSimpleName().length());
private static final String DEBUG_TAG = ClipboardStorage.class.getSimpleName().substring(0, TAG_LEN);

/**
*
Expand Down Expand Up @@ -58,7 +62,6 @@ public void copyTo(@NonNull List<OsmElement> elements, int latE7, int lonE7) {
selectionLat = latE7;
selectionLon = lonE7;
mode = Mode.COPY;

try {
for (OsmElement e : elements) {
storage.insertElementUnsafe(e);
Expand Down Expand Up @@ -99,6 +102,7 @@ public boolean isEmpty() {
public List<OsmElement> pasteFrom() {
List<Way> ways = storage.getWays();
List<Node> nodes = storage.getNodes();
List<Relation> relations = storage.getRelations();
List<OsmElement> result = new ArrayList<>();
if (mode == Mode.CUT) {
reset(); // can only paste a cut way once
Expand All @@ -113,6 +117,11 @@ public List<OsmElement> pasteFrom() {
result.add(w);
}
}
if (relations != null) {
for (Relation r : relations) {
result.add(r);
}
}
return result;
}

Expand Down
Loading
Loading