Skip to content

Commit 0c55662

Browse files
authored
Add drag'n'drop to the designer for importing files (#2586)
1 parent 62c088e commit 0c55662

File tree

3 files changed

+191
-34
lines changed

3 files changed

+191
-34
lines changed

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/actions/ToolImportAction.java

Lines changed: 46 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ This file is part of Universal Gcode Sender (UGS).
2323
import com.willwinder.ugs.nbp.designer.io.eagle.EaglePnpReader;
2424
import com.willwinder.ugs.nbp.designer.io.kicad.KiCadPosReader;
2525
import com.willwinder.ugs.nbp.designer.io.svg.SvgReader;
26+
import com.willwinder.ugs.nbp.designer.io.ugsd.UgsDesignReader;
2627
import com.willwinder.ugs.nbp.designer.logic.Controller;
2728
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
2829
import com.willwinder.ugs.nbp.designer.logic.Tool;
@@ -41,6 +42,7 @@ This file is part of Universal Gcode Sender (UGS).
4142
import javax.swing.filechooser.FileNameExtensionFilter;
4243
import java.awt.event.ActionEvent;
4344
import java.io.File;
45+
import java.util.Arrays;
4446
import java.util.Optional;
4547

4648
/**
@@ -54,6 +56,14 @@ This file is part of Universal Gcode Sender (UGS).
5456
displayName = "Import file",
5557
lazy = false)
5658
public final class ToolImportAction extends AbstractDesignAction {
59+
public static final FileNameExtensionFilter[] FILE_NAME_EXTENSION_FILTERS = new FileNameExtensionFilter[]{
60+
new FileNameExtensionFilter("Scalable Vector Graphics (.svg)", "svg"),
61+
new FileNameExtensionFilter("Autodesk CAD (.dxf)", "dxf"),
62+
new FileNameExtensionFilter("Carbide Create (.c2d)", "c2d"),
63+
new FileNameExtensionFilter("Eagle (.mnt, .mnb)", "mnt", "mnb"),
64+
new FileNameExtensionFilter("KiCad (.pos)", "pos"),
65+
new FileNameExtensionFilter("UGS design (.ugsd)", "ugsd")
66+
};
5767

5868
public static final String SMALL_ICON_PATH = "img/import.svg";
5969
public static final String LARGE_ICON_PATH = "img/import24.svg";
@@ -68,51 +78,53 @@ public ToolImportAction() {
6878
this.controller = ControllerFactory.getController();
6979
}
7080

81+
public static void readDesign(Controller controller, BackendAPI backend, File f) {
82+
Optional<Design> optionalDesign = Optional.empty();
83+
if (StringUtils.endsWithIgnoreCase(f.getName(), ".svg")) {
84+
SvgReader svgReader = new SvgReader();
85+
optionalDesign = svgReader.read(f);
86+
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".dxf")) {
87+
DxfReader reader = new DxfReader(backend.getSettings());
88+
optionalDesign = reader.read(f);
89+
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".c2d")) {
90+
C2dReader reader = new C2dReader();
91+
optionalDesign = reader.read(f);
92+
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") ||
93+
StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) {
94+
EaglePnpReader reader = new EaglePnpReader();
95+
optionalDesign = reader.read(f);
96+
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".pos")) {
97+
KiCadPosReader reader = new KiCadPosReader();
98+
optionalDesign = reader.read(f);
99+
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".ugsd")) {
100+
UgsDesignReader reader = new UgsDesignReader();
101+
optionalDesign = reader.read(f);
102+
}
103+
104+
if (optionalDesign.isPresent()) {
105+
Design design = optionalDesign.get();
106+
controller.setTool(Tool.SELECT);
107+
controller.addEntities(design.getEntities());
108+
controller.getSelectionManager().addSelection(design.getEntities());
109+
controller.getDrawing().repaint();
110+
} else {
111+
throw new RuntimeException("Could not open: " + f.getName());
112+
}
113+
}
114+
71115
@Override
72116
public void actionPerformed(ActionEvent e) {
73117
JFileChooser fileDialog = new JFileChooser();
74118
fileDialog.setFileSelectionMode(JFileChooser.FILES_ONLY);
75-
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Scalable Vector Graphics (.svg)", "svg"));
76-
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Autodesk CAD (.dxf)", "dxf"));
77-
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Carbide Create (.c2d)", "c2d"));
78-
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("Eagle (.mnt, .mnb)", "mnt", "mnb"));
79-
fileDialog.addChoosableFileFilter(new FileNameExtensionFilter("KiCad (.pos)", "pos"));
119+
Arrays.asList(FILE_NAME_EXTENSION_FILTERS).forEach(fileDialog::addChoosableFileFilter);
80120
fileDialog.showOpenDialog(SwingHelpers.getRootFrame());
81121

82122
BackendAPI backend = CentralLookup.getDefault().lookup(BackendAPI.class);
83123

84124
ThreadHelper.invokeLater(() -> {
85125
File f = fileDialog.getSelectedFile();
86126
if (f != null) {
87-
88-
Optional<Design> optionalDesign = Optional.empty();
89-
if (StringUtils.endsWithIgnoreCase(f.getName(), ".svg")) {
90-
SvgReader svgReader = new SvgReader();
91-
optionalDesign = svgReader.read(f);
92-
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".dxf")) {
93-
DxfReader reader = new DxfReader(backend.getSettings());
94-
optionalDesign = reader.read(f);
95-
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".c2d")) {
96-
C2dReader reader = new C2dReader();
97-
optionalDesign = reader.read(f);
98-
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".mnt") ||
99-
StringUtils.endsWithIgnoreCase(f.getName(), ".mnb")) {
100-
EaglePnpReader reader = new EaglePnpReader();
101-
optionalDesign = reader.read(f);
102-
} else if (StringUtils.endsWithIgnoreCase(f.getName(), ".pos")) {
103-
KiCadPosReader reader = new KiCadPosReader();
104-
optionalDesign = reader.read(f);
105-
}
106-
107-
if (optionalDesign.isPresent()) {
108-
Design design = optionalDesign.get();
109-
controller.setTool(Tool.SELECT);
110-
controller.addEntities(design.getEntities());
111-
controller.getSelectionManager().addSelection(design.getEntities());
112-
controller.getDrawing().repaint();
113-
} else {
114-
throw new RuntimeException("Could not open: " + f.getName());
115-
}
127+
readDesign(controller, backend, f);
116128
}
117129
});
118130
}

ugs-platform/ugs-platform-plugin-designer/src/main/java/com/willwinder/ugs/nbp/designer/gui/Drawing.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ This file is part of Universal Gcode Sender (UGS).
5050
import java.awt.Rectangle;
5151
import java.awt.RenderingHints;
5252
import java.awt.Shape;
53+
import java.awt.dnd.DnDConstants;
54+
import java.awt.dnd.DropTarget;
5355
import java.awt.geom.AffineTransform;
5456
import java.awt.geom.Point2D;
5557
import java.awt.geom.Rectangle2D;
@@ -77,6 +79,8 @@ public class Drawing extends JPanel {
7779
private double scale;
7880
private Point2D.Double position = new Point2D.Double();
7981
private Dimension oldMinimumSize;
82+
private transient DropHandler dropHandler;
83+
private transient DropTarget dropTarget;
8084

8185
public Drawing(Controller controller) {
8286
refreshThrottler = new Throttler(this::refresh, 1000);
@@ -115,6 +119,19 @@ public Drawing(Controller controller) {
115119
setScale(2);
116120
}
117121

122+
@Override
123+
public void addNotify() {
124+
super.addNotify();
125+
dropHandler = new DropHandler();
126+
dropTarget = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, dropHandler, true);
127+
}
128+
129+
@Override
130+
public void removeNotify() {
131+
super.removeNotify();
132+
dropTarget.removeDropTargetListener(dropHandler);
133+
}
134+
118135
public BufferedImage getImage() {
119136
BufferedImage bi = new BufferedImage(getPreferredSize().width,
120137
getPreferredSize().height, BufferedImage.TYPE_INT_RGB);
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
Copyright 2024 Will Winder
3+
4+
This file is part of Universal Gcode Sender (UGS).
5+
6+
UGS is free software: you can redistribute it and/or modify
7+
it under the terms of the GNU General Public License as published by
8+
the Free Software Foundation, either version 3 of the License, or
9+
(at your option) any later version.
10+
11+
UGS is distributed in the hope that it will be useful,
12+
but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
GNU General Public License for more details.
15+
16+
You should have received a copy of the GNU General Public License
17+
along with UGS. If not, see <http://www.gnu.org/licenses/>.
18+
*/
19+
package com.willwinder.ugs.nbp.designer.gui;
20+
21+
import com.willwinder.ugs.nbp.designer.actions.ToolImportAction;
22+
import static com.willwinder.ugs.nbp.designer.actions.ToolImportAction.FILE_NAME_EXTENSION_FILTERS;
23+
import com.willwinder.ugs.nbp.designer.logic.ControllerFactory;
24+
import com.willwinder.ugs.nbp.lib.lookup.CentralLookup;
25+
import com.willwinder.universalgcodesender.model.BackendAPI;
26+
import com.willwinder.universalgcodesender.utils.ThreadHelper;
27+
import org.apache.commons.lang3.StringUtils;
28+
29+
import java.awt.datatransfer.DataFlavor;
30+
import java.awt.datatransfer.Transferable;
31+
import java.awt.datatransfer.UnsupportedFlavorException;
32+
import java.awt.dnd.DnDConstants;
33+
import java.awt.dnd.DropTargetDragEvent;
34+
import java.awt.dnd.DropTargetDropEvent;
35+
import java.awt.dnd.DropTargetEvent;
36+
import java.awt.dnd.DropTargetListener;
37+
import java.io.File;
38+
import java.io.IOException;
39+
import java.util.Arrays;
40+
import java.util.Optional;
41+
42+
/**
43+
* Listens for drag'n'drop of known file types and imports it via the {@link ToolImportAction}.
44+
*
45+
* @author Joacim Breiler
46+
*/
47+
public class DropHandler implements DropTargetListener {
48+
private static Optional<String> getFilename(DataFlavor stringFlavor, Transferable transferable) {
49+
try {
50+
String filename = (String) transferable.getTransferData(stringFlavor);
51+
if (hasCorrectExtension(filename)) {
52+
return Optional.of(filename);
53+
}
54+
} catch (UnsupportedFlavorException | IOException e) {
55+
// Never mind...
56+
}
57+
58+
return Optional.empty();
59+
}
60+
61+
private static boolean hasCorrectExtension(String filename) {
62+
return Arrays.stream(FILE_NAME_EXTENSION_FILTERS)
63+
.flatMap(s -> Arrays.stream(s.getExtensions()))
64+
.anyMatch(extension -> StringUtils.endsWithIgnoreCase(filename, extension));
65+
}
66+
67+
private Optional<String> getFilename(DropTargetDragEvent dtde) {
68+
DataFlavor stringFlavor = DataFlavor.stringFlavor;
69+
if (!dtde.isDataFlavorSupported(stringFlavor)) {
70+
return Optional.empty();
71+
}
72+
73+
Transferable transferable = dtde.getTransferable();
74+
return getFilename(stringFlavor, transferable);
75+
}
76+
77+
private Optional<String> getFilename(DropTargetDropEvent dtde) {
78+
DataFlavor stringFlavor = DataFlavor.stringFlavor;
79+
if (!dtde.isDataFlavorSupported(stringFlavor)) {
80+
return Optional.empty();
81+
}
82+
83+
dtde.acceptDrop(DnDConstants.ACTION_COPY);
84+
Transferable transferable = dtde.getTransferable();
85+
return getFilename(stringFlavor, transferable);
86+
}
87+
88+
@Override
89+
public void dragEnter(DropTargetDragEvent dtde) {
90+
Optional<String> filename = getFilename(dtde);
91+
if (filename.isEmpty()) {
92+
dtde.rejectDrag();
93+
return;
94+
}
95+
96+
dtde.acceptDrag(DnDConstants.ACTION_COPY);
97+
}
98+
99+
@Override
100+
public void dragOver(DropTargetDragEvent dtde) {
101+
dragEnter(dtde);
102+
}
103+
104+
@Override
105+
public void dropActionChanged(DropTargetDragEvent dtde) {
106+
// Not used
107+
}
108+
109+
@Override
110+
public void dragExit(DropTargetEvent dte) {
111+
// Not used
112+
}
113+
114+
@Override
115+
public void drop(DropTargetDropEvent dtde) {
116+
Optional<String> filename = getFilename(dtde);
117+
if (filename.isEmpty()) {
118+
dtde.dropComplete(false);
119+
return;
120+
}
121+
122+
ThreadHelper.invokeLater(() -> {
123+
File file = filename.map(File::new).get();
124+
ToolImportAction.readDesign(ControllerFactory.getController(), CentralLookup.getDefault().lookup(BackendAPI.class), file);
125+
dtde.dropComplete(true);
126+
});
127+
}
128+
}

0 commit comments

Comments
 (0)