diff --git a/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/actions/ToolImportAction.java b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/actions/ToolImportAction.java index 0dee9f1b1..61739ffd3 100644 --- a/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/actions/ToolImportAction.java +++ b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/actions/ToolImportAction.java @@ -23,6 +23,7 @@ This file is part of Universal Gcode Sender (UGS). import com.willwinder.ugs.nbp.designer.io.eagle.EaglePnpReader; import com.willwinder.ugs.nbp.designer.io.kicad.KiCadPosReader; import com.willwinder.ugs.nbp.designer.io.svg.SvgReader; +import com.willwinder.ugs.nbp.designer.io.ugsd.UgsDesignReader; import com.willwinder.ugs.nbp.designer.logic.Controller; import com.willwinder.ugs.nbp.designer.logic.ControllerFactory; import com.willwinder.ugs.nbp.designer.logic.Tool; @@ -41,6 +42,7 @@ This file is part of Universal Gcode Sender (UGS). import javax.swing.filechooser.FileNameExtensionFilter; import java.awt.event.ActionEvent; import java.io.File; +import java.util.Arrays; import java.util.Optional; /** @@ -54,6 +56,14 @@ This file is part of Universal Gcode Sender (UGS). displayName = "Import file", lazy = false) public final class ToolImportAction extends AbstractDesignAction { + public static final FileNameExtensionFilter[] FILE_NAME_EXTENSION_FILTERS = new FileNameExtensionFilter[]{ + new FileNameExtensionFilter("Scalable Vector Graphics (.svg)", "svg"), + new FileNameExtensionFilter("Autodesk CAD (.dxf)", "dxf"), + new FileNameExtensionFilter("Carbide Create (.c2d)", "c2d"), + new FileNameExtensionFilter("Eagle (.mnt, .mnb)", "mnt", "mnb"), + new FileNameExtensionFilter("KiCad (.pos)", "pos"), + new FileNameExtensionFilter("UGS design (.ugsd)", "ugsd") + }; public static final String SMALL_ICON_PATH = "img/import.svg"; public static final String LARGE_ICON_PATH = "img/import24.svg"; @@ -68,15 +78,45 @@ public ToolImportAction() { this.controller = ControllerFactory.getController(); } + public static void readDesign(Controller controller, BackendAPI backend, File f) { + Optional optionalDesign = Optional.empty(); + if (StringUtils.endsWithIgnoreCase(f.getName(), ".svg")) { + SvgReader svgReader = new SvgReader(); + optionalDesign = svgReader.read(f); + } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".dxf")) { + DxfReader reader = new DxfReader(backend.getSettings()); + optionalDesign = reader.read(f); + } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".c2d")) { + C2dReader reader = new C2dReader(); + optionalDesign = reader.read(f); + } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") || + StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) { + EaglePnpReader reader = new EaglePnpReader(); + optionalDesign = reader.read(f); + } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".pos")) { + KiCadPosReader reader = new KiCadPosReader(); + optionalDesign = reader.read(f); + } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".ugsd")) { + UgsDesignReader reader = new UgsDesignReader(); + optionalDesign = reader.read(f); + } + + if (optionalDesign.isPresent()) { + Design design = optionalDesign.get(); + controller.setTool(Tool.SELECT); + controller.addEntities(design.getEntities()); + controller.getSelectionManager().addSelection(design.getEntities()); + controller.getDrawing().repaint(); + } else { + throw new RuntimeException("Could not open: " + f.getName()); + } + } + @Override public void actionPerformed(ActionEvent e) { JFileChooser fileDialog = new JFileChooser(); fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY); - fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Scalable Vector Graphics (.svg)", "svg")); - fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Autodesk CAD (.dxf)", "dxf")); - fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Carbide Create (.c2d)", "c2d")); - fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Eagle (.mnt, .mnb)", "mnt", "mnb")); - fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("KiCad (.pos)", "pos")); + Arrays.asList(FILE_NAME_EXTENSION_FILTERS).forEach(fileDialog::addChoosableFileFilter); fileDialog.showOpenDialog(SwingHelpers.getRootFrame()); BackendAPI backend = CentralLookup.getDefault().lookup(BackendAPI.class); @@ -84,35 +124,7 @@ public void actionPerformed(ActionEvent e) { ThreadHelper.invokeLater(() -> { File f = fileDialog.getSelectedFile(); if (f != null) { - - Optional optionalDesign = Optional.empty(); - if (StringUtils.endsWithIgnoreCase(f.getName(), ".svg")) { - SvgReader svgReader = new SvgReader(); - optionalDesign = svgReader.read(f); - } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".dxf")) { - DxfReader reader = new DxfReader(backend.getSettings()); - optionalDesign = reader.read(f); - } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".c2d")) { - C2dReader reader = new C2dReader(); - optionalDesign = reader.read(f); - } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") || - StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) { - EaglePnpReader reader = new EaglePnpReader(); - optionalDesign = reader.read(f); - } else if (StringUtils.endsWithIgnoreCase(f.getName(), ".pos")) { - KiCadPosReader reader = new KiCadPosReader(); - optionalDesign = reader.read(f); - } - - if (optionalDesign.isPresent()) { - Design design = optionalDesign.get(); - controller.setTool(Tool.SELECT); - controller.addEntities(design.getEntities()); - controller.getSelectionManager().addSelection(design.getEntities()); - controller.getDrawing().repaint(); - } else { - throw new RuntimeException("Could not open: " + f.getName()); - } + readDesign(controller, backend, f); } }); } diff --git a/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/Drawing.java b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/Drawing.java index 684e126d3..e2744b49c 100644 --- a/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/Drawing.java +++ b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/Drawing.java @@ -50,6 +50,8 @@ This file is part of Universal Gcode Sender (UGS). import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.Shape; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTarget; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; @@ -77,6 +79,8 @@ public class Drawing extends JPanel { private double scale; private Point2D.Double position = new Point2D.Double(); private Dimension oldMinimumSize; + private transient DropHandler dropHandler; + private transient DropTarget dropTarget; public Drawing(Controller controller) { refreshThrottler = new Throttler(this::refresh, 1000); @@ -115,6 +119,19 @@ public Drawing(Controller controller) { setScale(2); } + @Override + public void addNotify() { + super.addNotify(); + dropHandler = new DropHandler(); + dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, dropHandler, true); + } + + @Override + public void removeNotify() { + super.removeNotify(); + dropTarget.removeDropTargetListener(dropHandler); + } + public BufferedImage getImage() { BufferedImage bi = new BufferedImage(getPreferredSize().width, getPreferredSize().height, BufferedImage.TYPE_INT_RGB); diff --git a/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/DropHandler.java b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/DropHandler.java new file mode 100644 index 000000000..e05a70162 --- /dev/null +++ b/ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/DropHandler.java @@ -0,0 +1,128 @@ +/* + Copyright 2024 Will Winder + + This file is part of Universal Gcode Sender (UGS). + + UGS is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + UGS is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with UGS. If not, see . + */ +package com.willwinder.ugs.nbp.designer.gui; + +import com.willwinder.ugs.nbp.designer.actions.ToolImportAction; +import static com.willwinder.ugs.nbp.designer.actions.ToolImportAction.FILE_NAME_EXTENSION_FILTERS; +import com.willwinder.ugs.nbp.designer.logic.ControllerFactory; +import com.willwinder.ugs.nbp.lib.lookup.CentralLookup; +import com.willwinder.universalgcodesender.model.BackendAPI; +import com.willwinder.universalgcodesender.utils.ThreadHelper; +import org.apache.commons.lang3.StringUtils; + +import java.awt.datatransfer.DataFlavor; +import java.awt.datatransfer.Transferable; +import java.awt.datatransfer.UnsupportedFlavorException; +import java.awt.dnd.DnDConstants; +import java.awt.dnd.DropTargetDragEvent; +import java.awt.dnd.DropTargetDropEvent; +import java.awt.dnd.DropTargetEvent; +import java.awt.dnd.DropTargetListener; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.Optional; + +/** + * Listens for drag'n'drop of known file types and imports it via the {@link ToolImportAction}. + * + * @author Joacim Breiler + */ +public class DropHandler implements DropTargetListener { + private static Optional getFilename(DataFlavor stringFlavor, Transferable transferable) { + try { + String filename = (String) transferable.getTransferData(stringFlavor); + if (hasCorrectExtension(filename)) { + return Optional.of(filename); + } + } catch (UnsupportedFlavorException | IOException e) { + // Never mind... + } + + return Optional.empty(); + } + + private static boolean hasCorrectExtension(String filename) { + return Arrays.stream(FILE_NAME_EXTENSION_FILTERS) + .flatMap(s -> Arrays.stream(s.getExtensions())) + .anyMatch(extension -> StringUtils.endsWithIgnoreCase(filename, extension)); + } + + private Optional getFilename(DropTargetDragEvent dtde) { + DataFlavor stringFlavor = DataFlavor.stringFlavor; + if (!dtde.isDataFlavorSupported(stringFlavor)) { + return Optional.empty(); + } + + Transferable transferable = dtde.getTransferable(); + return getFilename(stringFlavor, transferable); + } + + private Optional getFilename(DropTargetDropEvent dtde) { + DataFlavor stringFlavor = DataFlavor.stringFlavor; + if (!dtde.isDataFlavorSupported(stringFlavor)) { + return Optional.empty(); + } + + dtde.acceptDrop(DnDConstants.ACTION_COPY); + Transferable transferable = dtde.getTransferable(); + return getFilename(stringFlavor, transferable); + } + + @Override + public void dragEnter(DropTargetDragEvent dtde) { + Optional filename = getFilename(dtde); + if (filename.isEmpty()) { + dtde.rejectDrag(); + return; + } + + dtde.acceptDrag(DnDConstants.ACTION_COPY); + } + + @Override + public void dragOver(DropTargetDragEvent dtde) { + dragEnter(dtde); + } + + @Override + public void dropActionChanged(DropTargetDragEvent dtde) { + // Not used + } + + @Override + public void dragExit(DropTargetEvent dte) { + // Not used + } + + @Override + public void drop(DropTargetDropEvent dtde) { + Optional filename = getFilename(dtde); + if (filename.isEmpty()) { + dtde.dropComplete(false); + return; + } + + ThreadHelper.invokeLater(() -> { + File file = filename.map(File::new).get(); + ToolImportAction.readDesign(ControllerFactory.getController(), CentralLookup.getDefault().lookup(BackendAPI.class), file); + dtde.dropComplete(true); + }); + } +} \ No newline at end of file