This is an automated email from the ASF dual-hosted git repository.
desruisseaux pushed a commit to branch geoapi-4.0
in repository https://gitbox.apache.org/repos/asf/sis.git
commit dfec2ae06d0a05a8c382b06687ed85592e42aa77
Author: Martin Desruisseaux <martin.desruisseaux@geomatys.com>
AuthorDate: Wed Sep 2 12:05:45 2020 +0200
Add a menu item for coyping the path to a file.
---
.../org/apache/sis/gui/dataset/ResourceTree.java | 81 ++++++++++++++++++++--
.../org/apache/sis/gui/dataset/WindowManager.java | 3 +-
.../main/java/org/apache/sis/gui/map/MapMenu.java | 7 +-
.../org/apache/sis/internal/gui/Resources.java | 15 ++--
.../apache/sis/internal/gui/Resources.properties | 3 +-
.../sis/internal/gui/Resources_fr.properties | 3 +-
.../apache/sis/internal/storage/URIDataStore.java | 44 +++++++++++-
.../sis/internal/storage/io/IOUtilities.java | 17 ++++-
8 files changed, 151 insertions(+), 22 deletions(-)
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
index 70d1133..12c1e1b 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceTree.java
@@ -17,6 +17,8 @@
package org.apache.sis.gui.dataset;
import java.io.File;
+import java.nio.file.Path;
+import java.net.URI;
import java.net.URL;
import java.net.MalformedURLException;
import java.util.AbstractList;
@@ -31,12 +33,15 @@ import javafx.concurrent.Task;
import javafx.collections.ObservableList;
import javafx.scene.control.Button;
import javafx.scene.control.ContextMenu;
+import javafx.scene.control.MenuItem;
import javafx.scene.control.TreeCell;
import javafx.scene.control.TreeItem;
import javafx.scene.control.TreeView;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;
+import javafx.scene.input.Clipboard;
+import javafx.scene.input.ClipboardContent;
import javafx.scene.paint.Color;
import org.opengis.util.GenericName;
import org.opengis.util.InternationalString;
@@ -53,13 +58,17 @@ import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreException;
import org.apache.sis.storage.event.StoreEvent;
import org.apache.sis.storage.event.StoreListener;
+import org.apache.sis.internal.storage.URIDataStore;
+import org.apache.sis.internal.storage.io.IOUtilities;
import org.apache.sis.internal.gui.ResourceLoader;
import org.apache.sis.internal.gui.BackgroundThreads;
import org.apache.sis.internal.gui.ExceptionReporter;
import org.apache.sis.internal.gui.LogHandler;
import org.apache.sis.internal.gui.Resources;
import org.apache.sis.internal.gui.Styles;
+import org.apache.sis.internal.system.Modules;
import org.apache.sis.internal.util.Strings;
+import org.apache.sis.util.logging.Logging;
/**
@@ -251,6 +260,46 @@ public class ResourceTree extends TreeView<Resource> {
}
/**
+ * Performs a "copy" action on the given resource. Current implementation performs only
+ * a "copy file path" action, but future versions may add more kinds of copy actions.
+ *
+ * @param resource the resource to copy.
+ */
+ private static void copy(final Resource resource) {
+ Object path;
+ try {
+ path = URIDataStore.location(resource);
+ } catch (DataStoreException e) {
+ ExceptionReporter.show(null, null, e);
+ return;
+ }
+ final ClipboardContent content = new ClipboardContent();
+ final boolean isKindOfPath = IOUtilities.isKindOfPath(path);
+ if (isKindOfPath || path instanceof CharSequence) {
+ String uri = path.toString();
+ String text = uri;
+ try {
+ if (path instanceof URI) {
+ path = new File((URI) path);
+ } else if (path instanceof Path) {
+ path = ((Path) path).toFile();
+ }
+ } catch (IllegalArgumentException | UnsupportedOperationException e) {
+ // Ignore
+ }
+ if (path instanceof File) {
+ content.putFiles(Collections.singletonList((File) path));
+ text = path.toString();
+ }
+ if (isKindOfPath) {
+ content.putUrl(uri);
+ }
+ content.putString(text);
+ }
+ Clipboard.getSystemClipboard().setContent(content);
+ }
+
+ /**
* Removes the given resource from the tree and closes it if it is a {@link DataStore}.
* It is caller's responsibility to ensure that the given resource is not used anymore.
* A resource can be removed only if it is a root. If the given resource is not in this
@@ -481,9 +530,7 @@ public class ResourceTree extends TreeView<Resource> {
setTextFill(isSelected() ? Styles.SELECTED_TEXT : color);
/*
* If the resource is at the root, add a menu for removing it.
- * If we find that the cell already has a menu, we do not need
- * to build it again (it may change in a future SIS version if
- * the menu is not always the same).
+ * If we find that the cell already has a menu, we do not need to build it again.
*/
if (tree != null) {
setText(text);
@@ -493,14 +540,38 @@ public class ResourceTree extends TreeView<Resource> {
menu = getContextMenu();
if (menu == null) {
menu = new ContextMenu();
- menu.getItems().add(tree.localized().menu(Resources.Keys.Close, (e)
-> {
+ final Resources localized = tree.localized();
+ final MenuItem[] items = new MenuItem[CLOSE + 1];
+ items[COPY_PATH] = localized.menu(Resources.Keys.CopyFilePath, (e)
-> {
+ copy(getItem());
+ });
+ items[CLOSE] = localized.menu(Resources.Keys.Close, (e) -> {
((ResourceTree) getTreeView()).removeAndClose(getItem());
- }));
+ });
+ menu.getItems().setAll(items);
}
+ /*
+ * "Copy file path" menu item should be enabled only if we can
+ * get some kind of file path or URI from the specified resource.
+ */
+ Object path;
+ try {
+ path = URIDataStore.location(resource);
+ } catch (DataStoreException e) {
+ path = null;
+ Logging.unexpectedException(Logging.getLogger(Modules.APPLICATION),
URIDataStore.class, "location", e);
+ }
+ menu.getItems().get(COPY_PATH).setDisable(!(IOUtilities.isKindOfPath(path)
|| path instanceof CharSequence));
}
setContextMenu(menu);
}
}
+
+ /**
+ * Position of menu items in the contextual menu built by {@link #updateItem(Resource,
boolean)}.
+ * Above method assumes that {@link #CLOSE} is the last menu item.
+ */
+ private static final int COPY_PATH = 0, CLOSE = 1;
}
/**
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java
index fcbf302..a19e4d8 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java
@@ -106,8 +106,7 @@ abstract class WindowManager extends Widget {
* @see #hasWindowsProperty
*/
public final MenuItem createNewWindowMenu() {
- final MenuItem menu = new MenuItem(localized().getString(Resources.Keys.NewWindow));
- menu.setOnAction(this::newDataWindow);
+ final MenuItem menu = localized().menu(Resources.Keys.NewWindow, this::newDataWindow);
menu.setDisable(true);
newWindowMenus.add(menu);
return menu;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapMenu.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapMenu.java
index 1af6a55..4e4f41c 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapMenu.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/map/MapMenu.java
@@ -159,8 +159,7 @@ public class MapMenu extends ContextMenu {
ArgumentChecks.ensureNonNull("format", format);
final MapCanvas.MenuHandler handler = startNewMenuItems(COPY);
final Resources resources = Resources.forLocale(canvas.getLocale());
- final MenuItem coordinates = new MenuItem(resources.getString(Resources.Keys.CoordinatesOfHere));
- coordinates.setOnAction((event) -> {
+ final MenuItem coordinates = resources.menu(Resources.Keys.CopyCoordinates, (event)
-> {
try {
final String text = format.formatCoordinates(handler.x, handler.y);
final ClipboardContent content = new ClipboardContent();
@@ -170,9 +169,7 @@ public class MapMenu extends ContextMenu {
ExceptionReporter.show(((MenuItem) event.getSource()).getText(), null, e);
}
});
- final Menu group = new Menu(resources.getString(Resources.Keys.Copy));
- group.getItems().setAll(coordinates);
- getItems().add(group);
+ getItems().add(coordinates);
}
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
index 16be644..4d4fb96 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.java
@@ -126,11 +126,6 @@ public final class Resources extends IndexedResourceBundle {
public static final short Close = 10;
/**
- * Coordinates of here
- */
- public static final short CoordinatesOfHere = 50;
-
- /**
* Copy
*/
public static final short Copy = 11;
@@ -141,6 +136,16 @@ public final class Resources extends IndexedResourceBundle {
public static final short CopyAs = 12;
/**
+ * Copy coordinates
+ */
+ public static final short CopyCoordinates = 50;
+
+ /**
+ * Copy file path
+ */
+ public static final short CopyFilePath = 51;
+
+ /**
* Display start
*/
public static final short DisplayStart = 38;
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
index f61ccc7..0c63818 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources.properties
@@ -34,9 +34,10 @@ CanNotRender = An error occurred while rendering the data.
CanNotUseRefSys_1 = Can not use the \u201c{0}\u201d reference system.
CenteredProjection = Centered projection
Close = Close
-CoordinatesOfHere = Coordinates of here
Copy = Copy
CopyAs = Copy as
+CopyCoordinates = Copy coordinates
+CopyFilePath = Copy file path
DisplayedSize = Displayed size
DisplayStart = Display start
DoesNotCoverAOI = Does not cover the area of interest.
diff --git a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
index fdda04f..cade986 100644
--- a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
+++ b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/Resources_fr.properties
@@ -39,9 +39,10 @@ CanNotRender = Une erreur est survenue lors de l\u2019affichage
des do
CanNotUseRefSys_1 = Ne peut pas utiliser le syst\u00e8me de r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
CenteredProjection = Projection centr\u00e9e
Close = Fermer
-CoordinatesOfHere = Coordonn\u00e9es d\u2019ici
Copy = Copier
CopyAs = Copier comme
+CopyCoordinates = Copier les coordonn\u00e9es
+CopyFilePath = Copier le chemin du fichier
DisplayedSize = Taille affich\u00e9e
DisplayStart = D\u00e9but de l\u2019affichage
DoesNotCoverAOI = Ne couvre pas la r\u00e9gion d\u2019int\u00e9r\u00eat.
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
index 069e503..19fc4d3 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/URIDataStore.java
@@ -28,6 +28,7 @@ import org.opengis.parameter.ParameterDescriptorGroup;
import org.opengis.parameter.ParameterNotFoundException;
import org.apache.sis.parameter.ParameterBuilder;
import org.apache.sis.storage.StorageConnector;
+import org.apache.sis.storage.Resource;
import org.apache.sis.storage.DataStore;
import org.apache.sis.storage.DataStoreProvider;
import org.apache.sis.storage.DataStoreException;
@@ -36,6 +37,8 @@ import org.apache.sis.storage.event.StoreEvent;
import org.apache.sis.storage.event.StoreListener;
import org.apache.sis.storage.event.WarningEvent;
import org.apache.sis.internal.storage.io.IOUtilities;
+import org.apache.sis.internal.system.Modules;
+import org.apache.sis.util.logging.Logging;
/**
@@ -45,7 +48,7 @@ import org.apache.sis.internal.storage.io.IOUtilities;
*
* @author Johann Sorel (Geomatys)
* @author Martin Desruisseaux (Geomatys)
- * @version 1.0
+ * @version 1.1
* @since 0.8
* @module
*/
@@ -100,7 +103,6 @@ public abstract class URIDataStore extends DataStore implements StoreResource,
R
return new Path[] {path};
}
-
/**
* Returns the parameters used to open this data store.
*
@@ -251,6 +253,44 @@ public abstract class URIDataStore extends DataStore implements StoreResource,
R
}
/**
+ * Returns the location (path, URL, URI, <i>etc.</i>) of the given resource.
+ * The type of the returned object can be any of the types documented in {@link DataStoreProvider#LOCATION}.
+ * The main ones are {@link java.net.URI}, {@link java.nio.file.Path} and JDBC {@linkplain
javax.sql.DataSource}.
+ *
+ * @param resource the resource for which to get the location, or {@code null}.
+ * @return location of the given resource, or {@code null} if none.
+ * @throws DataStoreException if an error on the file system prevent the creation of
the path.
+ *
+ * @since 1.1
+ */
+ public static Object location(final Resource resource) throws DataStoreException {
+ if (resource instanceof DataStore) {
+ final Optional<ParameterValueGroup> p = ((DataStore) resource).getOpenParameters();
+ if (p.isPresent()) try {
+ return p.get().parameter(DataStoreProvider.LOCATION).getValue();
+ } catch (ParameterNotFoundException e) {
+ /*
+ * This exception should not happen often since the "location" parameter
is recommended.
+ * Note that it does not mean the same thing than "parameter provided but
value is null".
+ * In that later case we want to return the null value as specified in the
parameters.
+ */
+ Logging.recoverableException(Logging.getLogger(Modules.STORAGE), URIDataStore.class,
"location", e);
+ }
+ }
+ /*
+ * This fallback should not happen with `URIDataStore` implementation because the
"location" parameter
+ * is always present even if null. This fallback is for resources implementated by
different classes.
+ */
+ if (resource instanceof ResourceOnFileSystem) {
+ final Path[] paths = ((ResourceOnFileSystem) resource).getComponentFiles();
+ if (paths != null && paths.length != 0) {
+ return paths[0]; // First path is presumed
the main file.
+ }
+ }
+ return null;
+ }
+
+ /**
* Adds the filename (without extension) as the citation title if there is no title,
or as the identifier otherwise.
* This method should be invoked last, after {@code DataStore} implementation did its
best effort for adding a title.
* The intent is actually to provide an identifier, but since the title is mandatory
in ISO 19115 metadata, providing
diff --git a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
index 0a2acc9..7ce731b 100644
--- a/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
+++ b/storage/sis-storage/src/main/java/org/apache/sis/internal/storage/io/IOUtilities.java
@@ -59,7 +59,7 @@ import org.apache.sis.internal.storage.Resources;
*
* @author Martin Desruisseaux (Geomatys)
* @author Johann Sorel (Geomatys)
- * @version 0.8
+ * @version 1.1
* @since 0.3
* @module
*/
@@ -71,6 +71,21 @@ public final class IOUtilities extends Static {
}
/**
+ * Returns {@code true} if the given object is a {@link Path}, {@link File}, {@link URL}
or {@link URI}.
+ * Note that this method does not returns {@code true} for {@link CharSequence}; that
type needs to be
+ * verified by the caller if desired.
+ *
+ * @param path the object to verify.
+ * @return whether the given object is of known type.
+ *
+ * @since 1.1
+ */
+ public static boolean isKindOfPath(final Object path) {
+ return (path instanceof URI) || (path instanceof URL) || // Test final classes
first.
+ (path instanceof Path) || (path instanceof File);
+ }
+
+ /**
* Returns the filename from a {@link Path}, {@link File}, {@link URL}, {@link URI} or
{@link CharSequence}
* instance. If the given argument is specialized type like {@code Path} or {@code File},
then this method uses
* dedicated API like {@link Path#getFileName()}. Otherwise this method gets a string
representation of the path
|