diff --git a/pom.xml b/pom.xml
index 091e663..c843436 100644
--- a/pom.xml
+++ b/pom.xml
@@ -112,6 +112,7 @@
1.3.2
0.1
+ 99.0
diff --git a/src/main/java/org/scijava/ui/swing/widget/SwingFileWidget.java b/src/main/java/org/scijava/ui/swing/widget/SwingFileWidget.java
index f669e39..9480dfd 100644
--- a/src/main/java/org/scijava/ui/swing/widget/SwingFileWidget.java
+++ b/src/main/java/org/scijava/ui/swing/widget/SwingFileWidget.java
@@ -321,7 +321,7 @@ public static List filterFiles(final List list,
// -- Helper classes --
- private class FileTransferHandler extends TransferHandler {
+ public static class FileTransferHandler extends TransferHandler {
private final String style;
diff --git a/src/main/java/org/scijava/ui/swing/widget/SwingURIWidget.java b/src/main/java/org/scijava/ui/swing/widget/SwingURIWidget.java
new file mode 100644
index 0000000..0506fcc
--- /dev/null
+++ b/src/main/java/org/scijava/ui/swing/widget/SwingURIWidget.java
@@ -0,0 +1,174 @@
+/*
+ * #%L
+ * SciJava UI components for Java Swing.
+ * %%
+ * Copyright (C) 2010 - 2023 SciJava developers.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+
+package org.scijava.ui.swing.widget;
+
+import org.scijava.plugin.Parameter;
+import org.scijava.plugin.Plugin;
+import org.scijava.ui.UIService;
+import org.scijava.widget.FileWidget;
+import org.scijava.widget.InputWidget;
+import org.scijava.widget.URIWidget;
+import org.scijava.widget.WidgetModel;
+
+import javax.swing.Box;
+import javax.swing.JButton;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.io.File;
+import java.net.URI;
+import java.net.URISyntaxException;
+
+/**
+ * Swing implementation of URI selector widget.
+ *
+ * @author Christian Tischer
+ * @author Jan Eglinger
+ */
+@Plugin(type = InputWidget.class)
+public class SwingURIWidget extends SwingInputWidget implements
+ URIWidget, ActionListener, DocumentListener
+{
+
+ @Parameter
+ private UIService uiService;
+
+ private JTextField uriTextField;
+ private JButton browse;
+
+ // -- InputWidget methods --
+
+ @Override
+ public URI getValue() {
+ final String text = uriTextField.getText();
+ if (text.isEmpty()) {
+ return null;
+ }
+ File file = new File(text);
+ try
+ {
+ if (file.exists() || file.isAbsolute()) {
+ return file.toURI();
+ }
+ return new URI(text);
+ } catch ( URISyntaxException e )
+ {
+ return null;
+ }
+ }
+
+ // -- WrapperPlugin methods --
+
+ @Override
+ public void set(final WidgetModel model) {
+ super.set(model);
+
+ uriTextField = new JTextField(16);
+ uriTextField.setDragEnabled(true);
+ final String style = model.getItem().getWidgetStyle();
+ uriTextField.setTransferHandler(new SwingFileWidget.FileTransferHandler(style));
+ setToolTip( uriTextField );
+ getComponent().add( uriTextField );
+ uriTextField.getDocument().addDocumentListener(this);
+
+ getComponent().add(Box.createHorizontalStrut(3));
+
+ browse = new JButton("Browse");
+ setToolTip(browse);
+ getComponent().add(browse);
+ browse.addActionListener(this);
+
+ refreshWidget();
+ }
+
+ // -- Typed methods --
+
+ @Override
+ public boolean supports(final WidgetModel model) {
+ return super.supports(model) && model.isType(URI.class);
+ }
+
+ // -- ActionListener methods --
+
+ @Override
+ public void actionPerformed(final ActionEvent e) {
+ File file = new File(uriTextField.getText());
+
+ if (!file.isDirectory()) {
+ file = file.getParentFile();
+ }
+
+ // display file chooser in appropriate mode
+ final WidgetModel model = get();
+ final String style;
+ if (model.isStyle(FileWidget.DIRECTORY_STYLE)) {
+ style = FileWidget.DIRECTORY_STYLE;
+ }
+ else if (model.isStyle(FileWidget.SAVE_STYLE)) {
+ style = FileWidget.SAVE_STYLE;
+ }
+ else {
+ style = FileWidget.OPEN_STYLE;
+ }
+ file = uiService.chooseFile(file, style);
+ if (file == null) return;
+
+ uriTextField.setText(file.getAbsolutePath());
+ }
+
+ // -- DocumentListener methods --
+
+ @Override
+ public void changedUpdate(final DocumentEvent e) {
+ updateModel();
+ }
+
+ @Override
+ public void insertUpdate(final DocumentEvent e) {
+ updateModel();
+ }
+
+ @Override
+ public void removeUpdate(final DocumentEvent e) {
+ updateModel();
+ }
+
+ // -- AbstractUIInputWidget methods ---
+
+ @Override
+ public void doRefresh() {
+// final String text = get().getText();
+// if (text.equals( uriTextField.getText())) return; // no change
+// uriTextField.setText(text);
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/URITest.java b/src/test/java/org/scijava/ui/swing/URITest.java
new file mode 100644
index 0000000..793c956
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/URITest.java
@@ -0,0 +1,26 @@
+package org.scijava.ui.swing;
+
+import java.io.File;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.net.URL;
+
+public class URITest
+{
+ public static void main( String[] args ) throws URISyntaxException, MalformedURLException
+ {
+ File f = new File("tischer");
+ System.out.println(f.toURI());
+ System.out.println( new URI( "file", "/tischer/test.txt" , null).toString() );
+ URI uri = new URI( "file:/path/to/local/file.txt" );
+ System.out.println( uri.getScheme() );
+ URL url = uri.toURL();
+ System.out.println(url.getFile());
+ if ( uri.getScheme().equals( "file" ) )
+ {
+ File file = new File( uri.toURL().getPath() );
+ System.out.println( file.getAbsolutePath() );
+ }
+ }
+}
diff --git a/src/test/java/org/scijava/ui/swing/widget/SwingURIWidgetDemo.java b/src/test/java/org/scijava/ui/swing/widget/SwingURIWidgetDemo.java
new file mode 100644
index 0000000..99c43e8
--- /dev/null
+++ b/src/test/java/org/scijava/ui/swing/widget/SwingURIWidgetDemo.java
@@ -0,0 +1,57 @@
+/*-
+ * #%L
+ * SciJava UI components for Java Swing.
+ * %%
+ * Copyright (C) 2010 - 2023 SciJava developers.
+ * %%
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ * #L%
+ */
+package org.scijava.ui.swing.widget;
+
+import org.scijava.Context;
+import org.scijava.command.Command;
+import org.scijava.command.CommandService;
+import org.scijava.plugin.Parameter;
+import org.scijava.ui.UIService;
+import org.scijava.widget.FileListWidget;
+import org.scijava.widget.FileWidget;
+
+import java.net.URI;
+
+public class SwingURIWidgetDemo implements Command {
+
+
+ @Parameter(style = FileListWidget.FILES_AND_DIRECTORIES, persist = false)
+ private URI uri;
+
+ @Override
+ public void run() {
+ System.out.println(uri);
+ }
+
+ public static void main(final String... args) throws Exception {
+ Context context = new Context();
+ context.service(UIService.class).showUI();
+ context.service(CommandService.class).run( SwingURIWidgetDemo.class, true);
+ }
+}