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 12036282c5601e68a7e7c0ec1a339c957d5c8fa6
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Tue Jan 21 12:20:21 2020 +0100

    Move window management in a separated WindowManager class.
---
 .../org/apache/sis/gui/dataset/FeatureList.java    |   2 +-
 .../org/apache/sis/gui/dataset/FeatureTable.java   |  10 +-
 .../apache/sis/gui/dataset/ResourceExplorer.java   | 174 +++-------------
 .../org/apache/sis/gui/dataset/WindowManager.java  | 230 +++++++++++++++++++++
 4 files changed, 263 insertions(+), 153 deletions(-)

diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureList.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureList.java
index f333939..256a01f 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureList.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureList.java
@@ -323,7 +323,7 @@ final class FeatureList extends ObservableListBase<Feature> 
{
     }
 
     /**
-     * If a loading process was under way, interrupts it and close the feature 
stream.
+     * If a loading process was under way, interrupts it and closes the 
feature stream.
      * This method returns immediately; the release of resources happens in a 
background thread.
      *
      * @see FeatureTable#interrupt()
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
index 4c5309a..044c3a6 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/FeatureTable.java
@@ -162,7 +162,7 @@ public class FeatureTable extends TableView<Feature> {
         if (items instanceof FeatureList) {
             return (FeatureList) items;
         } else {
-            return (FeatureList) ((ExpandableList) getItems()).getSource();
+            return (FeatureList) ((ExpandableList) items).getSource();
         }
     }
 
@@ -298,7 +298,7 @@ public class FeatureTable extends TableView<Feature> {
         } else {
             final ExpandableList list = getExpandableList();
             list.setMultivaluedColumns(multiValued);
-            final TableColumn<Feature,Feature> column = new TableColumn<>();
+            final TableColumn<Feature,Feature> column = new TableColumn<>("▤");
             column.setCellValueFactory(IdentityValueFactory.instance());
             column.setCellFactory(list);
             column.setReorderable(false);
@@ -378,6 +378,9 @@ public class FeatureTable extends TableView<Feature> {
          * @todo For {@link ValueCell} only (not {@link ElementCell}), if the 
feature is {@link ExpandedFeature}
          *       with {@code index != 0}, write text in gray. We could also 
use the value formatted at index 0
          *       for avoiding to format the same thing many times.
+         *
+         * @param  value  the new item for the cell.
+         * @param  empty  whether this cell is used to render an empty row.
          */
         @Override
         protected void updateItem(final Object value, final boolean empty) {
@@ -406,6 +409,9 @@ public class FeatureTable extends TableView<Feature> {
 
         /**
          * Invoked when a new value needs to be show.
+         *
+         * @param  value  the new item for the cell.
+         * @param  empty  whether this cell is used to render an empty row.
          */
         @Override
         protected void updateItem(Object value, final boolean empty) {
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
index deb27d5..d3f77c8 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/ResourceExplorer.java
@@ -16,23 +16,14 @@
  */
 package org.apache.sis.gui.dataset;
 
-import java.util.List;
-import java.util.ArrayList;
 import java.util.Collection;
-import javafx.beans.property.ReadOnlyBooleanProperty;
-import javafx.beans.property.ReadOnlyBooleanPropertyBase;
 import javafx.collections.ListChangeListener;
-import javafx.collections.ObservableList;
-import javafx.event.ActionEvent;
 import javafx.scene.layout.Region;
 import javafx.scene.control.ContextMenu;
-import javafx.scene.control.MenuItem;
-import javafx.scene.control.SeparatorMenuItem;
 import javafx.scene.control.SplitPane;
 import javafx.scene.control.Tab;
 import javafx.scene.control.TabPane;
 import javafx.scene.control.TreeItem;
-import javafx.stage.Stage;
 import org.apache.sis.storage.Resource;
 import org.apache.sis.storage.FeatureSet;
 import org.apache.sis.gui.metadata.MetadataSummary;
@@ -49,7 +40,7 @@ import org.apache.sis.internal.gui.Resources;
  * @since   1.1
  * @module
  */
-public class ResourceExplorer {
+public class ResourceExplorer extends WindowManager {
     /**
      * The tree of resources.
      */
@@ -74,59 +65,15 @@ public class ResourceExplorer {
     private final SplitPane content;
 
     /**
-     * The contextual menu items for showing data in a new window.
-     * This is disabled if there is no data to show.
-     */
-    private final List<MenuItem> windowMenus;
-
-    /**
-     * The menu items for navigating to different windows. {@code 
ResourceExplorer} will automatically
-     * add or remove elements in this list when new windows are created or 
closed.
-     *
-     * @see #setWindowsItems(ObservableList)
-     */
-    private ObservableList<MenuItem> windowsMenuItems;
-
-    /**
-     * A property telling whether at least one data window created by this 
{@code ResourceExplorer} is
-     * still visible.
-     *
-     * @see #createNewWindowMenu()
-     * @see #setWindowsItems(ObservableList)
-     */
-    public final ReadOnlyBooleanProperty hasWindowsProperty;
-
-    /**
-     * The {@link ResourceExplorer#hasWindowsProperty} property implementation.
-     */
-    private final class WindowsProperty extends ReadOnlyBooleanPropertyBase {
-        /** The property value. */
-        private boolean hasWindows;
-
-        /** Sets this property to the given value. */
-        final void set(final boolean value) {
-            hasWindows = value;
-            fireValueChangedEvent();
-        }
-
-        /** Returns the current property value. */
-        @Override public boolean get()    {return hasWindows;}
-        @Override public Object getBean() {return ResourceExplorer.this;}
-        @Override public String getName() {return "hasWindows";}
-    }
-
-    /**
      * Creates a new panel for exploring resources.
      */
     public ResourceExplorer() {
-        resources   = new ResourceTree();
-        metadata    = new MetadataSummary();
-        features    = new FeatureTable();
-        content     = new SplitPane();
-        windowMenus = new ArrayList<>(2);
-        hasWindowsProperty = new WindowsProperty();
+        resources = new ResourceTree();
+        metadata  = new MetadataSummary();
+        features  = new FeatureTable();
+        content   = new SplitPane();
 
-        final Resources localized = resources.localized;
+        final Resources localized = localized();
         final Tab dataTab = new Tab(localized.getString(Resources.Keys.Data), 
features);
         dataTab.setContextMenu(new ContextMenu(createNewWindowMenu()));
         final TabPane tabs = new TabPane(
@@ -144,54 +91,26 @@ public class ResourceExplorer {
     }
 
     /**
+     * Returns resources for current locale.
+     */
+    @Override
+    final Resources localized() {
+        return resources.localized;
+    }
+
+    /**
      * Returns the region containing the resource tree, metadata panel or any 
other control managed
      * by this {@code ResourceExplorer}. The subclass is implementation 
dependent and may change in
      * any future version.
      *
      * @return the region to show.
      */
+    @Override
     public final Region getView() {
         return content;
     }
 
     /**
-     * Creates a menu item for creating new windows for the currently selected 
resource.
-     * The new menu item is initially disabled. Its will become enabled 
automatically when
-     * a resource is selected.
-     *
-     * <p>Note: current implementation keeps a strong reference to created 
menu.
-     * Use this method only for menus that are expected to exist for 
application lifetime.</p>
-     *
-     * @return a "new window" menu item.
-     *
-     * @see #hasWindowsProperty
-     */
-    public final MenuItem createNewWindowMenu() {
-        final MenuItem menu = new 
MenuItem(resources.localized.getString(Resources.Keys.NewWindow));
-        menu.setOnAction(this::newDataWindow);
-        menu.setDisable(true);
-        windowMenus.add(menu);
-        return menu;
-    }
-
-    /**
-     * Sets the list where to add or remove the name of data windows. New data 
windows are created when
-     * user selects a menu item given by {@link #createNewWindowMenu()}. 
{@code ResourceExplorer} will
-     * automatically add or remove elements in the given list. The position of 
the new menu item will
-     * be just before the last {@link SeparatorMenuItem} instance. If no 
{@code SeparatorMenuItem} is
-     * found, then one will be inserted at the beginning of the given list 
when needed.
-     *
-     * @param  items  the list where to add and remove the name of windows.
-     *
-     * @see #hasWindowsProperty
-     * @see #createNewWindowMenu()
-     */
-    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
-    public void setWindowsItems(final ObservableList<MenuItem> items) {
-        windowsMenuItems = items;
-    }
-
-    /**
      * Loads all given sources in background threads and add them to the 
resource tree.
      * The given collection typically contains files to load,
      * but may also contain {@link Resource} instances to add directly.
@@ -227,66 +146,21 @@ public class ResourceExplorer {
         final FeatureSet data = (resource instanceof FeatureSet) ? 
(FeatureSet) resource : null;
         metadata.setMetadata(resource);
         features.setFeatures(data);
-        for (final MenuItem m : windowMenus) {
-            m.setDisable(data == null);
-        }
+        setNewWindowDisabled(data == null);
     }
 
     /**
-     * Invoked when user asked to show the data in a new window. This method 
may be invoked from various sources:
-     * contextual menu on the tab, contextual menu in the explorer tree, or 
from the "new window" menu item.
-     *
-     * @param  event  ignored (can be {@code null}).
+     * Returns the set of currently selected data, or {@code null} if none.
      */
-    private void newDataWindow(final ActionEvent event) {
+    @Override
+    final SelectedData getSelectedData() {
         final FeatureSet data = features.getFeatures();
         if (data != null) {
-            final String title = resources.getTitle(data, false);
-            final DataWindow window = new DataWindow((Stage) 
content.getScene().getWindow(), features);
-            window.setTitle(title + " — Apache SIS");
-            window.show();
-            if (windowsMenuItems != null) {
-                /*
-                 * Search for insertion point just before the menu separator.
-                 * If no menu separator is found, add one.
-                 */
-                int insertAt = windowsMenuItems.size();
-                do if (--insertAt < 0) {
-                    windowsMenuItems.add(insertAt = 0, new 
SeparatorMenuItem());
-                    ((WindowsProperty) hasWindowsProperty).set(true);
-                    break;
-                } while (!(windowsMenuItems.get(insertAt) instanceof 
SeparatorMenuItem));
-                final MenuItem menu = new MenuItem(title);
-                menu.setOnAction((e) -> window.toFront());
-                windowsMenuItems.add(insertAt, menu);
-                window.setOnHidden((e) -> removeDataWindow(menu));
-            }
-        }
-    }
-
-    /**
-     * Invoked when a window has been hidden. This method removes the window 
title from the "Windows" menu.
-     * The hidden window will be garbage collected at some later time.
-     */
-    private void removeDataWindow(final MenuItem menu) {
-        final ObservableList<MenuItem> items = windowsMenuItems;
-        if (items != null) {
-            for (int i = items.size(); --i >= 0;) {
-                if (items.get(i) == menu) {
-                    items.remove(i);
-                    if (i == 0) {
-                        if (!items.isEmpty()) {
-                            if (items.get(0) instanceof SeparatorMenuItem) {
-                                items.remove(0);
-                            } else {
-                                break;      // Some other windows are still 
present.
-                            }
-                        }
-                        ((WindowsProperty) hasWindowsProperty).set(false);
-                    }
-                    break;
-                }
-            }
+            final SelectedData selection = new SelectedData();
+            selection.title = resources.getTitle(data, false);
+            selection.features = features;
+            return selection;
         }
+        return null;
     }
 }
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
new file mode 100644
index 0000000..6d2970d
--- /dev/null
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/dataset/WindowManager.java
@@ -0,0 +1,230 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.sis.gui.dataset;
+
+import java.util.List;
+import java.util.ArrayList;
+import javafx.collections.ObservableList;
+import javafx.stage.Stage;
+import javafx.event.ActionEvent;
+import javafx.scene.layout.Region;
+import javafx.scene.control.MenuItem;
+import javafx.scene.control.SeparatorMenuItem;
+import javafx.beans.property.ReadOnlyBooleanProperty;
+import javafx.beans.property.ReadOnlyBooleanPropertyBase;
+import org.apache.sis.internal.gui.Resources;
+
+
+/**
+ * Manages the list of opened {@link DataWindow}s.
+ *
+ * @author  Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since   1.1
+ * @module
+ */
+abstract class WindowManager {
+    /**
+     * The contextual menu items for creating a new window showing selected 
data.
+     * All menus in this list will be enabled or disabled depending on whether 
there is data to show.
+     */
+    private final List<MenuItem> newWindowMenus;
+
+    /**
+     * The menu items for navigating to different windows. {@link 
WindowManager} will automatically
+     * add or remove elements in that list when new windows are created or 
closed.
+     *
+     * @see #setWindowsItems(ObservableList)
+     */
+    private ObservableList<MenuItem> showWindowMenus;
+
+    /**
+     * A property telling whether at least one data window created by this 
class is still visible.
+     *
+     * @see #createNewWindowMenu()
+     * @see #setWindowsItems(ObservableList)
+     */
+    public final ReadOnlyBooleanProperty hasWindowsProperty;
+
+    /**
+     * The {@link WindowManager#hasWindowsProperty} property implementation.
+     */
+    private final class WindowsProperty extends ReadOnlyBooleanPropertyBase {
+        /** The property value. */
+        private boolean hasWindows;
+
+        /** Sets this property to the given value. */
+        final void set(final boolean value) {
+            hasWindows = value;
+            fireValueChangedEvent();
+        }
+
+        /** Returns the current property value. */
+        @Override public boolean get()    {return hasWindows;}
+        @Override public Object getBean() {return WindowManager.this;}
+        @Override public String getName() {return "hasWindows";}
+    }
+
+    /**
+     * Creates a new manager of windows.
+     */
+    WindowManager() {
+        newWindowMenus     = new ArrayList<>(2);
+        hasWindowsProperty = new WindowsProperty();
+    }
+
+    /**
+     * Returns resources for current locale. We could fetch this information 
ourselves,
+     * but we currently ask to subclass because it has this information anyway.
+     */
+    abstract Resources localized();
+
+    /**
+     * Returns the control shown in the main window.
+     */
+    abstract Region getView();
+
+    /**
+     * Creates a menu item for creating new windows for the currently selected 
resource.
+     * The new menu item is initially disabled. Its will become enabled 
automatically when
+     * a resource is selected.
+     *
+     * <p>Note: current implementation keeps a strong reference to created 
menu.
+     * Use this method only for menus that are expected to exist for 
application lifetime.</p>
+     *
+     * @return a "new window" menu item.
+     *
+     * @see #hasWindowsProperty
+     */
+    public final MenuItem createNewWindowMenu() {
+        final MenuItem menu = new 
MenuItem(localized().getString(Resources.Keys.NewWindow));
+        menu.setOnAction(this::newDataWindow);
+        menu.setDisable(true);
+        newWindowMenus.add(menu);
+        return menu;
+    }
+
+    /**
+     * Sets the list where to add or remove the name of data windows. New data 
windows are created when user
+     * selects a menu item given by {@link #createNewWindowMenu()}. {@code 
WindowManager} will automatically
+     * add or remove elements in the given list. The position of the new menu 
item will be just before the
+     * last {@link SeparatorMenuItem} instance. If no {@code 
SeparatorMenuItem} is found, then one will be
+     * inserted at the beginning of the given list when needed.
+     *
+     * @param  items  the list where to add and remove the name of windows.
+     *
+     * @see #hasWindowsProperty
+     * @see #createNewWindowMenu()
+     */
+    @SuppressWarnings("AssignmentToCollectionOrArrayFieldFromParameter")
+    public void setWindowsItems(final ObservableList<MenuItem> items) {
+        showWindowMenus = items;
+    }
+
+    /**
+     * Enables or disables all "new window" menus. Those menu should be 
disabled when the current selection
+     * does not contain any data we can show.
+     */
+    final void setNewWindowDisabled(final boolean disabled) {
+        for (final MenuItem m : newWindowMenus) {
+            m.setDisable(disabled);
+        }
+    }
+
+    /**
+     * A description of currently selected data.
+     */
+    static final class SelectedData {
+        /**
+         * A title to use for windows and menu items.
+         */
+        String title;
+
+        /**
+         * The control that contains the currently selected data.
+         */
+        FeatureTable features;
+
+        /**
+         * Creates an initially empty set of selected data.
+         */
+        SelectedData() {
+        }
+    }
+
+    /**
+     * Returns the set of currently selected data, or {@code null} if none.
+     */
+    abstract SelectedData getSelectedData();
+
+    /**
+     * Invoked when user asked to show the data in a new window. This method 
may be invoked from various sources:
+     * contextual menu on the tab, contextual menu in the explorer tree, or 
from the "new window" menu item.
+     *
+     * @param  event  ignored (can be {@code null}).
+     */
+    private void newDataWindow(final ActionEvent event) {
+        final SelectedData selection = getSelectedData();
+        if (selection != null) {
+            final DataWindow window = new DataWindow((Stage) 
getView().getScene().getWindow(), selection.features);
+            window.setTitle(selection.title + " — Apache SIS");
+            window.show();
+            if (showWindowMenus != null) {
+                /*
+                 * Search for insertion point just before the menu separator.
+                 * If no menu separator is found, add one.
+                 */
+                int insertAt = showWindowMenus.size();
+                do if (--insertAt < 0) {
+                    showWindowMenus.add(insertAt = 0, new SeparatorMenuItem());
+                    ((WindowsProperty) hasWindowsProperty).set(true);
+                    break;
+                } while (!(showWindowMenus.get(insertAt) instanceof 
SeparatorMenuItem));
+                final MenuItem menu = new MenuItem(selection.title);
+                menu.setOnAction((e) -> window.toFront());
+                showWindowMenus.add(insertAt, menu);
+                window.setOnHidden((e) -> removeDataWindow(menu));
+            }
+        }
+    }
+
+    /**
+     * Invoked when a window has been hidden. This method removes the window 
title from the "Windows" menu.
+     * The hidden window will be garbage collected at some later time.
+     */
+    private void removeDataWindow(final MenuItem menu) {
+        final ObservableList<MenuItem> items = showWindowMenus;
+        if (items != null) {
+            for (int i = items.size(); --i >= 0;) {
+                if (items.get(i) == menu) {
+                    items.remove(i);
+                    if (i == 0) {
+                        if (!items.isEmpty()) {
+                            if (items.get(0) instanceof SeparatorMenuItem) {
+                                items.remove(0);
+                            } else {
+                                break;      // Some other windows are still 
present.
+                            }
+                        }
+                        ((WindowsProperty) hasWindowsProperty).set(false);
+                    }
+                    break;
+                }
+            }
+        }
+    }
+}

Reply via email to