From be169107679fd125717b32f9b7d5e86b67af1784 Mon Sep 17 00:00:00 2001 From: Bane Stojkovic Date: Mon, 19 Jan 2026 23:15:46 +0100 Subject: [PATCH] fix copy paste groups --- .../ComponentTransferableFactory.java | 13 +++++-- .../java/org/diylc/core/ComponentGroup.java | 7 +++- .../src/main/java/org/diylc/core/Project.java | 9 ++++- .../diylc/presenter/InstantiationManager.java | 10 ++++- .../java/org/diylc/presenter/Presenter.java | 37 +++++++++++++++++-- .../diylc-core/src/main/resources/update.xml | 4 ++ 6 files changed, 69 insertions(+), 11 deletions(-) diff --git a/diylc/diylc-core/src/main/java/org/diylc/clipboard/ComponentTransferableFactory.java b/diylc/diylc-core/src/main/java/org/diylc/clipboard/ComponentTransferableFactory.java index bde929c6..8d13fe07 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/clipboard/ComponentTransferableFactory.java +++ b/diylc/diylc-core/src/main/java/org/diylc/clipboard/ComponentTransferableFactory.java @@ -57,9 +57,16 @@ public class ComponentTransferableFactory { .filter(Objects::nonNull) .collect(Collectors.toList()); - Set clonedGroups = groups.stream().map(group -> - new ComponentGroup(group.getComponentIds().stream() - .map(idMap::get).collect(Collectors.toSet()), group.getName())) + Set clonedGroups = groups.stream() + .map(group -> { + Set newIds = group.getComponentIds().stream() + .map(idMap::get) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + // Only create group if it has at least one valid ID + return newIds.isEmpty() ? null : new ComponentGroup(newIds, group.getName()); + }) + .filter(Objects::nonNull) .collect(Collectors.toSet()); return new ComponentTransferable(clonedComponents, clonedGroups); diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/ComponentGroup.java b/diylc/diylc-core/src/main/java/org/diylc/core/ComponentGroup.java index 058b697c..43bb90e5 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/core/ComponentGroup.java +++ b/diylc/diylc-core/src/main/java/org/diylc/core/ComponentGroup.java @@ -22,8 +22,11 @@ public class ComponentGroup implements Serializable { } public static ComponentGroup from(Collection> components, String name) { - return new ComponentGroup(components.stream() - .map(IDIYComponent::getId).collect(Collectors.toSet()), name); + Set componentIds = components.stream() + .map(IDIYComponent::getId) + .filter(id -> id != null) + .collect(Collectors.toSet()); + return new ComponentGroup(componentIds, name); } public Set getComponentIds() { diff --git a/diylc/diylc-core/src/main/java/org/diylc/core/Project.java b/diylc/diylc-core/src/main/java/org/diylc/core/Project.java index 3d68714d..e58eb052 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/core/Project.java +++ b/diylc/diylc-core/src/main/java/org/diylc/core/Project.java @@ -366,7 +366,14 @@ public class Project implements Serializable, Cloneable { } for (ComponentGroup group : this.getGroupsEx()) { - project.getGroupsEx().add(new ComponentGroup(group.getComponentIds(), group.getName())); + // Filter out null IDs when cloning groups + Set validIds = group.getComponentIds().stream() + .filter(id -> id != null) + .collect(Collectors.toSet()); + // Only add group if it has valid component IDs + if (!validIds.isEmpty()) { + project.getGroupsEx().add(new ComponentGroup(validIds, group.getName())); + } } for (IDIYComponent component : this.getLockedComponents()) { diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/InstantiationManager.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/InstantiationManager.java index e44c035c..f5b7b6b0 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/InstantiationManager.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/InstantiationManager.java @@ -34,6 +34,7 @@ import org.diylc.clipboard.ComponentTransferable; import org.diylc.common.*; import org.diylc.core.Angle; +import org.diylc.core.ComponentGroup; import org.diylc.core.CreationMethod; import org.diylc.core.IDIYComponent; import org.diylc.core.Project; @@ -219,9 +220,14 @@ public class InstantiationManager { // Update the location according to mouse location updateSingleClick(scaledPoint, snapToGrid, gridSpacing); - // copy group information if available + // copy group information if available, but filter out invalid groups if (componentTransferable.getGroups() != null) { - currentProject.getGroupsEx().addAll(componentTransferable.getGroups()); + for (ComponentGroup group : componentTransferable.getGroups()) { + if (group.getComponentIds() != null && !group.getComponentIds().isEmpty() && + group.getComponentIds().stream().anyMatch(id -> id != null)) { + currentProject.getGroupsEx().add(group); + } + } } } diff --git a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java index 5c161f62..93e98861 100644 --- a/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java +++ b/diylc/diylc-core/src/main/java/org/diylc/presenter/Presenter.java @@ -278,6 +278,8 @@ public class Presenter implements IPlugInPort { drawingManager.clearContinuityArea(); DrawingCache.Instance.clear(); updateSelection(EMPTY_SELECTION); + // Clean up any invalid groups that may have been loaded + cleanupInvalidGroups(); messageDispatcher.dispatchMessage(EventType.PROJECT_LOADED, project, freshStart, filename); messageDispatcher.dispatchMessage(EventType.REPAINT); messageDispatcher.dispatchMessage(EventType.LAYER_STATE_CHANGED, currentProject.getLockedLayers()); @@ -543,7 +545,11 @@ public class Presenter implements IPlugInPort { // group components if there's more than one, e.g. building blocks, but not clipboard // contents if (componentSlot.size() > 1 && !componentTypeSlot.getName().toLowerCase().contains("clipboard")) { - this.currentProject.getGroupsEx().add(ComponentGroup.from(componentSlot, componentTypeSlot.getName())); + ComponentGroup group = ComponentGroup.from(componentSlot, componentTypeSlot.getName()); + // Only add group if it has valid component IDs + if (group.getComponentIds() != null && !group.getComponentIds().isEmpty()) { + this.currentProject.getGroupsEx().add(group); + } } notifyProjectModifiedIfNeeded(oldProject, "Add " + componentTypeSlot.getName(), true, true); @@ -1717,6 +1723,10 @@ public class Presenter implements IPlugInPort { @Override public void groupSelectedComponents() { LOG.info("groupSelectedComponents()"); + + if (selectedComponents == null || selectedComponents.size() < 2) { + return; + } // Prompt user for group name String groupName = view.showInputDialog("Enter group name:", "Group Components"); @@ -1739,9 +1749,11 @@ public class Presenter implements IPlugInPort { .filter(id -> id != null) .collect(Collectors.toSet()); - // Only create the group if there are valid component IDs + // Only create the group if there are valid component IDs if (!componentIds.isEmpty()) { currentProject.getGroupsEx().add(new ComponentGroup(componentIds, groupName)); + } else { + LOG.warn("Cannot create group: all selected components have null IDs"); } // Notify the listeners. notifyProjectModifiedIfNeeded(oldProject, "Group", false, true); @@ -2197,12 +2209,31 @@ public class Presenter implements IPlugInPort { * @param components */ private void ungroupComponents(Collection> components) { - Set componentIds = components.stream().map(IDIYComponent::getId).collect(Collectors.toSet()); + Set componentIds = components.stream() + .map(IDIYComponent::getId) + .filter(id -> id != null) + .collect(Collectors.toSet()); // Remove the group if it contains any of the specified components currentProject.getGroupsEx().removeIf( group -> group.getComponentIds() != null && group.getComponentIds().stream() .anyMatch(id -> id != null && componentIds.contains(id))); } + + /** + * Removes invalid groups (groups with null IDs or empty sets) from the project. + * This should be called periodically to clean up corrupted groups. + */ + private void cleanupInvalidGroups() { + int initialSize = currentProject.getGroupsEx().size(); + currentProject.getGroupsEx().removeIf(group -> + group.getComponentIds() == null || + group.getComponentIds().isEmpty() || + group.getComponentIds().stream().allMatch(id -> id == null)); + int removed = initialSize - currentProject.getGroupsEx().size(); + if (removed > 0) { + LOG.warn("Removed " + removed + " invalid group(s) with null or empty component IDs"); + } + } /** * Finds all components that are grouped with the specified component. This should be called any diff --git a/diylc/diylc-core/src/main/resources/update.xml b/diylc/diylc-core/src/main/resources/update.xml index 870b79a1..fe7bce8b 100644 --- a/diylc/diylc-core/src/main/resources/update.xml +++ b/diylc/diylc-core/src/main/resources/update.xml @@ -573,6 +573,10 @@ NEW_FEATURE Added E-C-B BJT pinout to TO-92 transistor package + + BUG_FIX + Fixed the performance issue that happens when groups of components are copy-pasted and the project size grows uncontrollably + BUG_FIX 'Tactile Micro Switch' editor doesn't show up on double click