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 aa78fbc72ea01823805b4e6d106f3b6dbbd3c57a Author: Martin Desruisseaux <martin.desruisse...@geomatys.com> AuthorDate: Tue Dec 28 02:23:30 2021 +0100 Improvements in the handling of "Copy" actions from contextual menu. --- .../org/apache/sis/gui/metadata/MetadataTree.java | 81 ++++++++++++++++++++-- .../sis/gui/metadata/StandardMetadataTree.java | 73 +++++++------------ 2 files changed, 100 insertions(+), 54 deletions(-) diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java index 3d322fa..4c2f9cb 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/MetadataTree.java @@ -29,15 +29,23 @@ import javafx.beans.property.ReadOnlyStringWrapper; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ObservableValue; import javafx.collections.ObservableList; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; import javafx.scene.control.TreeItem; +import javafx.scene.control.TreeTableRow; import javafx.scene.control.TreeTableView; import javafx.scene.control.TreeTableColumn; import javafx.scene.control.TreeTableColumn.CellDataFeatures; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; import org.opengis.util.InternationalString; import org.opengis.referencing.IdentifiedObject; import org.apache.sis.referencing.IdentifiedObjects; import org.apache.sis.internal.util.PropertyFormat; import org.apache.sis.internal.system.Modules; +import org.apache.sis.internal.gui.Resources; import org.apache.sis.util.collection.TreeTable; import org.apache.sis.util.collection.TableColumn; import org.apache.sis.util.resources.Vocabulary; @@ -64,7 +72,7 @@ import org.apache.sis.util.logging.Logging; * For changing content, use the {@link #contentProperty} instead.</li> * </ul> * - * @todo Add a panel for controlling the number/date/angle format pattern. + * @todo Add menu items for controlling the number/date/angle format pattern. * * @author Siddhesh Rane (GSoC) * @author Martin Desruisseaux (Geomatys) @@ -89,7 +97,7 @@ public class MetadataTree extends TreeTableView<TreeTable.Node> { * The column for metadata property value in the model. * This is usually {@link TableColumn#VALUE} or {@link TableColumn#VALUE_AS_TEXT}. */ - TableColumn<?> valueSourceColumn; + private TableColumn<?> valueSourceColumn; /** * The data shown in this tree table. The {@link ObjectProperty#set(Object)} method requires @@ -146,7 +154,7 @@ check: if (data != null) { * Creates a new initially empty metadata tree. */ public MetadataTree() { - this(null, false); + this(null); } /** @@ -157,6 +165,8 @@ check: if (data != null) { */ public MetadataTree(final MetadataSummary controller) { this(controller, false); + setRowFactory(Row::new); + setShowRoot(false); } /** @@ -183,9 +193,6 @@ check: if (data != null) { setColumnResizePolicy(CONSTRAINED_RESIZE_POLICY); getColumns().setAll(nameColumn, valueColumn); contentProperty.addListener(MetadataTree::applyChange); - if (!standard) { - setShowRoot(false); - } } /** @@ -375,4 +382,66 @@ check: if (data != null) { return new ReadOnlyObjectWrapper<>(value); } } + + /** + * A row in a metadata tree view, used for adding contextual menu on a row-by-row basis. + */ + static class Row extends TreeTableRow<TreeTable.Node> implements EventHandler<ActionEvent> { + /** + * The context menu, to be added only if this row is non-empty. + */ + protected final ContextMenu menu; + + /** + * The menu item for copying current row. + */ + protected final MenuItem copy; + + /** + * Creates a new row for the given tree table. + */ + @SuppressWarnings("ThisEscapedInObjectConstruction") + Row(final TreeTableView<TreeTable.Node> view) { + final MetadataTree md = (MetadataTree) view; + final Resources localized = Resources.forLocale(md.getLocale()); + copy = new MenuItem(localized.getString(Resources.Keys.Copy)); + menu = new ContextMenu(copy); + copy.setOnAction(this); + } + + /** + * Invoked when a new row is selected. This method sets the contextual menu on the row. + */ + @Override + protected void updateItem(final TreeTable.Node item, final boolean empty) { + super.updateItem(item, empty); + setContextMenu(empty ? null : menu); + copy.setDisable(empty || getValue() == null); + } + + /** + * Returns the object in the "value" column of current row, or {@code null} if none. + */ + private Object getValue() { + final TreeTable.Node node = getItem(); + if (node != null) { + final Object obj = node.getUserObject(); + return (obj != null) ? obj : node.getValue(((MetadataTree) getTreeTableView()).valueSourceColumn); + } + return null; + } + + /** + * Invoked when user selected a menu item. + */ + @Override + public void handle(final ActionEvent event) { + final Object value = getValue(); + if (value != null) { + final ClipboardContent content = new ClipboardContent(); + content.putString(value.toString()); + Clipboard.getSystemClipboard().setContent(content); + } + } + } } diff --git a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java index e46986e..afe4730 100644 --- a/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java +++ b/application/sis-javafx/src/main/java/org/apache/sis/gui/metadata/StandardMetadataTree.java @@ -20,12 +20,9 @@ import java.util.Collections; import java.io.StringWriter; import javax.xml.transform.stream.StreamResult; import javafx.event.ActionEvent; -import javafx.event.EventHandler; -import javafx.scene.control.ContextMenu; import javafx.scene.control.Menu; import javafx.scene.control.MenuItem; import javafx.scene.control.TreeTableView; -import javafx.scene.control.TreeTableRow; import javafx.scene.input.Clipboard; import javafx.scene.input.ClipboardContent; import org.opengis.metadata.Metadata; @@ -67,17 +64,12 @@ import org.apache.sis.xml.XML; * * @author Siddhesh Rane (GSoC) * @author Martin Desruisseaux (Geomatys) - * @version 1.1 + * @version 1.2 * @since 1.1 * @module */ public class StandardMetadataTree extends MetadataTree { /** - * The "copy" and "copy as" localized string, used for contextual menus. - */ - private final String copy, copyAs; - - /** * Creates a new initially empty metadata tree. */ public StandardMetadataTree() { @@ -94,9 +86,6 @@ public class StandardMetadataTree extends MetadataTree { */ public StandardMetadataTree(final MetadataSummary controller) { super(controller, true); - final Resources localized = Resources.forLocale(getLocale()); - copy = localized.getString(Resources.Keys.Copy); - copyAs = localized.getString(Resources.Keys.CopyAs); setRowFactory(Row::new); if (controller != null) { controller.metadataProperty.addListener((p,o,n) -> setContent(n)); @@ -104,43 +93,35 @@ public class StandardMetadataTree extends MetadataTree { } /** - * Returns the given metadata as a tree table. - */ - private static TreeTable toTree(final Object metadata) { - if (metadata instanceof AbstractMetadata) { - return ((AbstractMetadata) metadata).asTreeTable(); - } else { - // `COMPACT` is the default policy of `AbstractMetadata.asTreeTable()`. - return MetadataStandard.ISO_19115.asTreeTable(metadata, null, ValueExistencePolicy.COMPACT); - } - } - - /** * Sets the metadata to show in this tree table. This method gets a {@link TreeTable} view * of the given metadata, then delegates to {@link #setContent(TreeTable)}. * * @param metadata the metadata to show in this tree table view, or {@code null} if none. */ public void setContent(final Metadata metadata) { - setContent(metadata == null ? null : toTree(metadata)); + final TreeTable tree; + if (metadata == null) { + tree = null; + } else if (metadata instanceof AbstractMetadata) { + tree = ((AbstractMetadata) metadata).asTreeTable(); + } else { + // `COMPACT` is the default policy of `AbstractMetadata.asTreeTable()`. + tree = MetadataStandard.ISO_19115.asTreeTable(metadata, null, ValueExistencePolicy.COMPACT); + } + setContent(tree); } /** * A row in a metadata tree view, used for adding contextual menu on a row-by-row basis. */ - private static final class Row extends TreeTableRow<TreeTable.Node> implements EventHandler<ActionEvent> { - /** - * The context menu, to be added only if this row is non-empty. - */ - private final ContextMenu menu; - + private static final class Row extends MetadataTree.Row { /** * The menu items for XML or WKT formats. */ private final MenuItem copyAsXML, copyAsLegacy, copyAsWKT; /** - * The menu items for copying in XML formats, to be disabled if we can not do this export. + * The group of menu items for copying in various formats, to be disabled if we can not do this export. */ private final Menu copyAs; @@ -149,21 +130,21 @@ public class StandardMetadataTree extends MetadataTree { */ @SuppressWarnings("ThisEscapedInObjectConstruction") Row(final TreeTableView<TreeTable.Node> view) { + super(view); final StandardMetadataTree md = (StandardMetadataTree) view; - final MenuItem copy; - copy = new MenuItem(md.copy); + final Resources localized = Resources.forLocale(md.getLocale()); copyAsXML = new MenuItem(); copyAsWKT = new MenuItem("WKT — Well Known Text"); copyAsLegacy = new MenuItem("XML — Metadata (2007)"); - copyAs = new Menu(md.copyAs, null, copyAsWKT, copyAsXML, copyAsLegacy); - menu = new ContextMenu(copy, copyAs); + copyAs = new Menu(localized.getString(Resources.Keys.CopyAs), null, copyAsWKT, copyAsXML, copyAsLegacy); + menu .getItems().add(copyAs); copyAsLegacy.setOnAction(this); copyAsXML .setOnAction(this); - copy .setOnAction(this); + copyAsWKT .setOnAction(this); } /** - * Invoked when a new row is selected. This method enable or disable the "copy as" menu + * Invoked when a new row is selected. This method enables or disables the "copy as" menu * depending on whether or not we can format XML document for currently selected row. */ @Override @@ -190,7 +171,6 @@ public class StandardMetadataTree extends MetadataTree { } copyAs.setDisable(disabled); } - setContextMenu(empty ? null : menu); } /** @@ -203,7 +183,9 @@ public class StandardMetadataTree extends MetadataTree { final TreeTable.Node node = getItem(); if (node != null) { final Object obj = node.getUserObject(); - if (obj != null) { + if (obj == null) { + super.handle(event); + } else { final Object source = event.getSource(); final ClipboardContent content = new ClipboardContent(); final String text; @@ -220,21 +202,16 @@ public class StandardMetadataTree extends MetadataTree { Collections.singletonMap(XML.METADATA_VERSION, LegacyNamespaces.VERSION_2007)); text = output.toString(); content.put(DataFormats.ISO_19139, text); - } else if (MetadataStandard.ISO_19115.isMetadata(obj.getClass())) { - text = toTree(obj).toString(); } else { - final Object value = node.getValue(((MetadataTree) getTreeTableView()).valueSourceColumn); - if (value == null) return; - text = value.toString(); + text = obj.toString(); } + content.putString(text); + Clipboard.getSystemClipboard().setContent(content); } catch (Exception e) { final Resources localized = Resources.forLocale(((MetadataTree) getTreeTableView()).getLocale()); ExceptionReporter.show(this, localized.getString(Resources.Keys.ErrorExportingData), localized.getString(Resources.Keys.CanNotCreateXML), e); - return; } - content.putString(text); - Clipboard.getSystemClipboard().setContent(content); } } }