Skip to content

Commit b6b7924

Browse files
authored
Merge pull request #34 from scijava/improve-file-list-widget
Improve file list widget
2 parents 6e2c988 + 8077b53 commit b6b7924

File tree

1 file changed

+128
-17
lines changed

1 file changed

+128
-17
lines changed

src/main/java/org/scijava/ui/swing/widget/SwingFileListWidget.java

Lines changed: 128 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,25 @@
3333
package org.scijava.ui.swing.widget;
3434

3535
import java.awt.Dimension;
36+
import java.awt.dnd.DnDConstants;
37+
import java.awt.dnd.DropTarget;
38+
import java.awt.dnd.DropTargetAdapter;
39+
import java.awt.dnd.DropTargetDragEvent;
40+
import java.awt.dnd.DropTargetDropEvent;
41+
import java.awt.dnd.DropTargetEvent;
3642
import java.awt.event.ActionEvent;
3743
import java.awt.event.ActionListener;
3844
import java.awt.event.MouseEvent;
3945
import java.awt.event.MouseListener;
4046
import java.io.File;
4147
import java.io.FileFilter;
48+
import java.io.IOException;
49+
import java.nio.file.Files;
50+
import java.util.ArrayList;
51+
import java.util.Arrays;
4252
import java.util.Collections;
4353
import java.util.List;
54+
import java.util.TooManyListenersException;
4455

4556
import javax.swing.Box;
4657
import javax.swing.DefaultListModel;
@@ -53,10 +64,13 @@
5364
import net.miginfocom.swing.MigLayout;
5465

5566
import org.scijava.log.LogService;
67+
import org.scijava.module.ModuleService;
5668
import org.scijava.plugin.Parameter;
5769
import org.scijava.plugin.Plugin;
70+
import org.scijava.thread.ThreadService;
5871
import org.scijava.ui.UIService;
5972
import org.scijava.widget.FileListWidget;
73+
import org.scijava.widget.FileWidget;
6074
import org.scijava.widget.InputWidget;
6175
import org.scijava.widget.WidgetModel;
6276

@@ -70,13 +84,17 @@ public class SwingFileListWidget extends SwingInputWidget<File[]> implements
7084
FileListWidget<JPanel>, ActionListener, MouseListener {
7185

7286
@Parameter
73-
private UIService uiService;
87+
private LogService logService;
7488

7589
@Parameter
76-
private LogService logService;
90+
private ModuleService moduleService;
91+
92+
@Parameter
93+
private ThreadService threadService;
7794

7895
private JList<File> paths;
7996
private JButton addFilesButton;
97+
private JButton addFolderButton;
8098
private JButton removeFilesButton;
8199
private JButton clearButton;
82100

@@ -107,14 +125,34 @@ public void set(final WidgetModel model) {
107125

108126
getComponent().add(Box.createHorizontalStrut(3));
109127

110-
// GroupLayout buttonLayout = new GroupLayout(getComponent());
111128
JPanel buttonPanel = new JPanel(new MigLayout());
112129

113-
addFilesButton = new JButton("Add files...");
130+
// Set button label dependent on widget style
131+
String filesLabel = model.isStyle(DIRECTORIES_ONLY) ? "Add folders..."
132+
: "Add files...";
133+
addFilesButton = new JButton(filesLabel);
114134
setToolTip(addFilesButton);
115135
buttonPanel.add(addFilesButton, "wrap, grow");
116136
addFilesButton.addActionListener(this);
117137

138+
// Only show folder button if style allows files
139+
if (!model.isStyle(DIRECTORIES_ONLY)) {
140+
addFolderButton = new JButton("Add folder content...");
141+
setToolTip(addFolderButton);
142+
// add TransferHandler to accept dropped folders
143+
addFolderButton.setTransferHandler(new FolderTransferHandler());
144+
145+
DropTarget dropTarget = addFolderButton.getDropTarget();
146+
try {
147+
dropTarget.addDropTargetListener(new ButtonDropTargetListener());
148+
} catch (TooManyListenersException exc) {
149+
logService.error("Error with setting up drop support", exc);
150+
}
151+
152+
buttonPanel.add(addFolderButton, "wrap, grow");
153+
addFolderButton.addActionListener(this);
154+
}
155+
118156
removeFilesButton = new JButton("Remove selected");
119157
setToolTip(removeFilesButton);
120158
buttonPanel.add(removeFilesButton, "wrap, grow");
@@ -127,14 +165,6 @@ public void set(final WidgetModel model) {
127165

128166
getComponent().add(buttonPanel);
129167

130-
/*
131-
getComponent().setLayout(buttonLayout);
132-
buttonLayout.setVerticalGroup(buttonLayout.createSequentialGroup()
133-
.addComponent(addFilesButton)
134-
.addComponent(removeFilesButton)
135-
.addComponent(clearButton));
136-
*/
137-
138168
refreshWidget();
139169
}
140170

@@ -151,12 +181,12 @@ public boolean supports(final WidgetModel model) {
151181
public void actionPerformed(final ActionEvent e) {
152182
DefaultListModel<File> listModel = //
153183
(DefaultListModel<File>) paths.getModel();
154-
List<File> fileList = Collections.list(listModel.elements());
155184

156185
if (e.getSource() == addFilesButton) {
157186
// Add new files
158187
// parse style attribute to allow choosing
159188
// files and/or directories, and filter files
189+
List<File> fileList = Collections.list(listModel.elements());
160190
final WidgetModel widgetModel = get();
161191
final String widgetStyle = widgetModel.getItem().getWidgetStyle();
162192
FileFilter filter = SwingFileWidget.createFileFilter(widgetStyle);
@@ -170,12 +200,16 @@ public void actionPerformed(final ActionEvent e) {
170200
style = FileListWidget.FILES_ONLY; // default
171201
}
172202

173-
fileList = uiService.chooseFiles(null, fileList, filter, style);
203+
fileList = ui().chooseFiles(null, fileList, filter, style);
174204
if (fileList == null)
175205
return;
176-
for (File file : fileList) {
177-
listModel.addElement(file);
178-
}
206+
fileList.forEach(file -> listModel.addElement(file));
207+
} else if (e.getSource() == addFolderButton) {
208+
File folder = ui().chooseFile(null, FileWidget.DIRECTORY_STYLE);
209+
if (folder == null)
210+
return;
211+
List<File> fileList = getFilesFromFolder(folder);
212+
fileList.forEach(file -> listModel.addElement(file));
179213
} else if (e.getSource() == removeFilesButton) {
180214
// Remove selected files
181215
List<File> selected = paths.getSelectedValuesList();
@@ -240,6 +274,27 @@ public void mouseExited(MouseEvent e) {
240274
// Nothing to do
241275
}
242276

277+
// -- Helper methods --
278+
279+
private List<File> getFilesFromFolder(File inputFolder) {
280+
// get files in folder and add to listModel
281+
List<File> fileList = new ArrayList<>();
282+
final WidgetModel widgetModel = get();
283+
final String widgetStyle = widgetModel.getItem().getWidgetStyle();
284+
FileFilter filter = SwingFileWidget.createFileFilter(widgetStyle);
285+
try {
286+
fileList = Arrays
287+
.asList((Files.walk(inputFolder.toPath())
288+
.filter(path -> filter.accept(path.toFile())))
289+
.map(path -> path.toFile())
290+
.toArray(File[]::new));
291+
} catch (IOException exc) {
292+
logService
293+
.error("Error when trying to retrieve file list", exc);
294+
}
295+
return fileList;
296+
}
297+
243298
// -- Helper classes --
244299

245300
private class FileListTransferHandler extends TransferHandler {
@@ -275,4 +330,60 @@ public boolean importData(final TransferHandler.TransferSupport support) {
275330
return true;
276331
}
277332
}
333+
334+
private class FolderTransferHandler extends TransferHandler {
335+
@Override
336+
public boolean canImport(final TransferHandler.TransferSupport support) {
337+
return SwingFileWidget.hasFiles(support);
338+
}
339+
340+
@Override
341+
public boolean importData(final TransferHandler.TransferSupport support) {
342+
// getFiles from support
343+
final List<File> files = SwingFileWidget.getFiles(support);
344+
if (files == null || files.size() != 1)
345+
return false;
346+
347+
// check if it's a folder
348+
final File folder = files.get(0);
349+
if (!folder.isDirectory())
350+
return false;
351+
352+
// get all files matching filter from folder and subfolders
353+
List<File> fileList = getFilesFromFolder(folder);
354+
355+
// add files to model
356+
DefaultListModel<File> model = //
357+
(DefaultListModel<File>) paths.getModel();
358+
fileList.forEach(file -> model.addElement(file));
359+
paths.setModel(model);
360+
updateModel();
361+
return true;
362+
}
363+
}
364+
365+
private class ButtonDropTargetListener extends DropTargetAdapter {
366+
367+
@Override
368+
public void drop(DropTargetDropEvent dtde) {
369+
JButton button = (JButton) dtde.getDropTargetContext()
370+
.getComponent();
371+
button.getModel().setPressed(false);
372+
dtde.acceptDrop(DnDConstants.ACTION_COPY_OR_MOVE);
373+
}
374+
375+
@Override
376+
public void dragEnter(DropTargetDragEvent dtde) {
377+
JButton button = (JButton) dtde.getDropTargetContext()
378+
.getComponent();
379+
button.getModel().setPressed(true);
380+
}
381+
382+
@Override
383+
public void dragExit(DropTargetEvent dte) {
384+
JButton button = (JButton) dte.getDropTargetContext()
385+
.getComponent();
386+
button.getModel().setPressed(false);
387+
}
388+
}
278389
}

0 commit comments

Comments
 (0)