sis-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From desruisse...@apache.org
Subject [sis] 01/04: Add a menu item for coyping the path to a file.
Date Wed, 02 Sep 2020 14:09:55 GMT
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


Mime
View raw message