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


The following commit(s) were added to refs/heads/geoapi-4.0 by this push:
     new a3b8326446 Move to `MapContextview` the widgets controlling the 
coverage appearence. Move to the status bar the label which was showing the CRS 
name.
a3b8326446 is described below

commit a3b83264468f82d9cf4a39503336d8492cec14db
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Mar 30 17:56:23 2026 +0200

    Move to `MapContextview` the widgets controlling the coverage appearence.
    Move to the status bar the label which was showing the CRS name.
---
 .../apache/sis/gui/coverage/CoverageControls.java  |  98 +++++----------
 .../apache/sis/gui/coverage/StyleController.java   |  92 +++++++++++++-
 .../apache/sis/gui/coverage/ViewAndControls.java   |  10 +-
 .../org/apache/sis/gui/internal/Resources.java     |   5 +
 .../apache/sis/gui/internal/Resources.properties   |   1 +
 .../sis/gui/internal/Resources_fr.properties       |   1 +
 .../main/org/apache/sis/gui/internal/Styles.java   |   6 +
 .../main/org/apache/sis/gui/map/MapMenu.java       |  25 ++--
 .../main/org/apache/sis/gui/map/StatusBar.java     | 136 ++++++++++++++++-----
 9 files changed, 258 insertions(+), 116 deletions(-)

diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageControls.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageControls.java
index a3ea19acd9..f53be4ff3a 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageControls.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/CoverageControls.java
@@ -18,16 +18,10 @@ package org.apache.sis.gui.coverage;
 
 import java.util.Locale;
 import javafx.application.Platform;
-import javafx.scene.control.TitledPane;
-import javafx.scene.control.ChoiceBox;
-import javafx.scene.control.Label;
-import javafx.scene.control.TableView;
 import javafx.scene.control.Tooltip;
-import javafx.scene.layout.GridPane;
-import javafx.scene.layout.VBox;
-import javafx.scene.layout.Priority;
+import javafx.scene.control.TitledPane;
+import javafx.beans.value.ObservableObjectValue;
 import javafx.collections.ObservableList;
-import org.apache.sis.image.Interpolation;
 import org.apache.sis.coverage.Category;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.storage.GridCoverageResource;
@@ -36,8 +30,6 @@ import org.apache.sis.gui.dataset.WindowHandler;
 import org.apache.sis.gui.map.MapMenu;
 import org.apache.sis.gui.map.style.MapLayer;
 import org.apache.sis.gui.map.style.MapContextView;
-import org.apache.sis.gui.internal.GUIUtilities;
-import org.apache.sis.gui.internal.Styles;
 import org.apache.sis.gui.internal.Resources;
 import org.apache.sis.gui.internal.DataStoreOpener;
 import org.apache.sis.gui.internal.BackgroundThreads;
@@ -69,24 +61,9 @@ final class CoverageControls extends ViewAndControls {
     private final StyleController style;
 
     /**
-     * The control showing categories and their colors for the current 
coverage.
-     */
-    private final TableView<Category> categoryTable;
-
-    /**
-     * The control used for selecting a color ramp for a given category.
-     */
-    private final CoverageStyling styling;
-
-    /**
-     * The control used for selecting a color stretching mode.
-     */
-    private final ChoiceBox<Stretching> stretching;
-
-    /**
-     * The control used for selecting the interpolation method.
+     * Tool tip for the reference system shown in the status bar.
      */
-    private final ChoiceBox<Interpolation> interpolation;
+    private final Tooltip referenceSystemTooltip;
 
     /**
      * Creates a new set of coverage controls.
@@ -105,6 +82,14 @@ final class CoverageControls extends ViewAndControls {
         final MapMenu menu = new MapMenu(view);
         menu.addReferenceSystems(owner.referenceSystems);
         menu.addCopyOptions(status);
+        final ObservableObjectValue<String> selectedReferenceSystem = 
menu.selectedReferenceSystem().orElse(null);
+        if (selectedReferenceSystem == null) {
+            referenceSystemTooltip = null;
+        } else {
+            referenceSystemTooltip = new 
Tooltip(resources.getString(Resources.Keys.SelectCrsByContextMenu));
+            selectedReferenceSystem.addListener((p,o,n) -> 
notifyReferenceSystemChanged(n, status.positionReferenceSystemName().get()));
+            status.positionReferenceSystemName().addListener((p,o,n) -> 
notifyReferenceSystemChanged(selectedReferenceSystem.get(), n));
+        }
         /*
          * "Layers" section with the following controls:
          *    - Tree of layers associated to the coverage (styling, isolines, 
visual indication of loaded tiles).
@@ -112,43 +97,6 @@ final class CoverageControls extends ViewAndControls {
         final var layers = new MapContextView(resources);
         style = new StyleController(view);
         layers.setRootItem(style);
-        /*
-         * "Display" section with the following controls:
-         *    - Current CRS
-         *    - Interpolation
-         *    - Color stretching
-         *    - Colors for each category
-         */
-        final VBox displayPane;
-        {   // Block for making variables locale to this scope.
-            final Label crsControl = new Label();
-            crsControl.setPadding(CONTENT_MARGIN);
-            crsControl.setTooltip(new 
Tooltip(resources.getString(Resources.Keys.SelectCrsByContextMenu)));
-            menu.selectedReferenceSystem().ifPresent((text) -> 
crsControl.textProperty().bind(text));
-            /*
-             * Creates a "Values" sub-section with the following controls:
-             *   - Interpolation
-             *   - Color stretching
-             */
-            interpolation = InterpolationConverter.button(view);
-            stretching = Stretching.createButton((p,o,n) -> 
view.setStretching(n));
-            final GridPane valuesControl = Styles.createControlGrid(0,
-                label(vocabulary, Vocabulary.Keys.Interpolation, 
interpolation),
-                label(vocabulary, Vocabulary.Keys.Stretching, stretching));
-            /*
-             * Creates a "Categories" section with the category table.
-             */
-            styling = new CoverageStyling(view);
-            categoryTable = styling.createCategoryTable(resources, vocabulary);
-            VBox.setVgrow(categoryTable, Priority.ALWAYS);
-            /*
-             * All sections put together.
-             */
-            displayPane = new VBox(
-                    labelOfGroup(vocabulary, Vocabulary.Keys.ReferenceSystem, 
crsControl,    true),  crsControl,
-                    labelOfGroup(vocabulary, Vocabulary.Keys.Values,          
valuesControl, false), valuesControl,
-                    labelOfGroup(vocabulary, Vocabulary.Keys.Categories,      
categoryTable, false), categoryTable);
-        }
         /*
          * "Isolines" section with the following controls:
          *    - Colors for each isoline levels
@@ -170,8 +118,7 @@ final class CoverageControls extends ViewAndControls {
          */
         final TitledPane deferred;                  // Control to be built 
only if requested.
         controlPanes = new TitledPane[] {
-            new TitledPane(vocabulary.getString(Vocabulary.Keys.Layers),  
layers.getView()),
-            new TitledPane(vocabulary.getString(Vocabulary.Keys.Display), 
displayPane),
+            new TitledPane(vocabulary.getString(Vocabulary.Keys.Layers), 
layers.getView()),
             new TitledPane(resources.getString(Resources.Keys.Windows), 
windows.getView()),
             deferred = new 
TitledPane(vocabulary.getString(Vocabulary.Keys.Properties), null)
         };
@@ -186,6 +133,19 @@ final class CoverageControls extends ViewAndControls {
         setView(view.getView());
     }
 
+    /**
+     * Invoked in JavaFX thread after the reference system of the coverage or 
of the status bar changed.
+     *
+     * @param coverage      the name of the reference system of the rendered 
coverage.
+     * @param coordinates   the name of the reference system of coordinates 
shown in the status bar.
+     */
+    private void notifyReferenceSystemChanged(String coverage, String 
coordinates) {
+        if (coverage != null && !coverage.equalsIgnoreCase(coordinates)) {
+            coverage = 
Resources.forLocale(owner.getLocale()).getString(Resources.Keys.MismatchedRS);
+        }
+        status.setDefaultMessage(coverage, coverage == null ? null : 
referenceSystemTooltip);
+    }
+
     /**
      * Invoked in JavaFX thread after {@link CoverageCanvas} resource or 
coverage property value changed.
      * This method updates the controls GUI with new information available and 
update the corresponding
@@ -215,7 +175,7 @@ final class CoverageControls extends ViewAndControls {
                 }
             });
         });
-        final ObservableList<Category> items = categoryTable.getItems();
+        final ObservableList<Category> items = style.categoryTable.getItems();
         if (coverage == null) {
             items.clear();
         } else {
@@ -242,8 +202,6 @@ final class CoverageControls extends ViewAndControls {
      * This is invoked when the user click on "New window" button.
      */
     final void copyStyling(final CoverageControls c) {
-        styling.copyStyling(c.styling);
-        GUIUtilities.copySelection(c.stretching, stretching);
-        GUIUtilities.copySelection(c.interpolation, interpolation);
+        style.copyStyling(c.style);
     }
 }
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/StyleController.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/StyleController.java
index 514f2aa75e..11db66b533 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/StyleController.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/StyleController.java
@@ -16,14 +16,26 @@
  */
 package org.apache.sis.gui.coverage;
 
+import java.util.Locale;
 import javafx.scene.control.TreeItem;
+import javafx.scene.control.ChoiceBox;
 import javafx.collections.ObservableList;
-import org.apache.sis.gui.internal.Resources;
+import javafx.scene.control.TableView;
+import javafx.scene.layout.GridPane;
+import javafx.scene.layout.Priority;
+import javafx.scene.layout.Region;
+import javafx.scene.layout.VBox;
 import org.apache.sis.gui.map.style.MapItem;
 import org.apache.sis.gui.map.style.ItemController;
 import org.apache.sis.gui.map.style.MapLayer;
+import org.apache.sis.gui.internal.Resources;
+import org.apache.sis.gui.internal.Styles;
+import org.apache.sis.gui.internal.GUIUtilities;
+import org.apache.sis.image.Interpolation;
+import org.apache.sis.coverage.Category;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.storage.tiling.TiledGridCoverageResource;
+import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
@@ -37,6 +49,32 @@ final class StyleController extends ItemController {
      */
     private final CoverageCanvas canvas;
 
+    /**
+     * The controls for configuring the appearance of the grid coverage.
+     * Created when first needed.
+     */
+    private final Region configurationPanel;
+
+    /**
+     * The control showing categories and their colors for the current 
coverage.
+     */
+    final TableView<Category> categoryTable;
+
+    /**
+     * The control used for selecting a color ramp for a given category.
+     */
+    private final CoverageStyling styling;
+
+    /**
+     * The control used for selecting a color stretching mode.
+     */
+    private final ChoiceBox<Stretching> stretching;
+
+    /**
+     * The control used for selecting the interpolation method.
+     */
+    private final ChoiceBox<Interpolation> interpolation;
+
     /**
      * Whether to show a visual indication of which tiles are read.
      * Creates when first needed.
@@ -54,6 +92,58 @@ final class StyleController extends ItemController {
         setSelected(true);
         setIndependent(true);
         selectedProperty().addListener((p,o,n) -> 
canvas.setCoverageHidden(!n));
+        /*
+         * "Display" section with the following controls:
+         *    - Current CRS
+         *    - Interpolation
+         *    - Color stretching
+         *    - Colors for each category
+         */
+        final Locale     locale     = canvas.getLocale();
+        final Resources  resources  = Resources.forLocale(locale);
+        final Vocabulary vocabulary = Vocabulary.forLocale(locale);
+        /*
+         * Creates a "Values" sub-section with the following controls:
+         *   - Interpolation
+         *   - Color stretching
+         */
+        interpolation = InterpolationConverter.button(canvas);
+        stretching = Stretching.createButton((p,o,n) -> 
canvas.setStretching(n));
+        final GridPane valuesControl = Styles.createControlGrid(0,
+                CoverageControls.label(vocabulary, 
Vocabulary.Keys.Interpolation, interpolation),
+                CoverageControls.label(vocabulary, Vocabulary.Keys.Stretching, 
stretching));
+        /*
+         * Creates a "Categories" section with the category table.
+         */
+        styling = new CoverageStyling(canvas);
+        categoryTable = styling.createCategoryTable(resources, vocabulary);
+        VBox.setVgrow(categoryTable, Priority.ALWAYS);
+        /*
+         * All sections put together.
+         */
+        configurationPanel = new VBox(
+                CoverageControls.labelOfGroup(vocabulary, 
Vocabulary.Keys.Values,     valuesControl, true),  valuesControl,
+                CoverageControls.labelOfGroup(vocabulary, 
Vocabulary.Keys.Categories, categoryTable, false), categoryTable);
+    }
+
+    /**
+     * Returns a panel of JavaFX controls for configuring the appearance of 
the grid coverage.
+     *
+     * @return the configuration panel for the grid coverage.
+     */
+    @Override
+    protected Region getConfigurationPanel() {
+        return configurationPanel;
+    }
+
+    /**
+     * Copies the styling configuration from the given controls.
+     * This is invoked when the user click on "New window" button.
+     */
+    final void copyStyling(final StyleController c) {
+        styling.copyStyling(c.styling);
+        GUIUtilities.copySelection(c.stretching, stretching);
+        GUIUtilities.copySelection(c.interpolation, interpolation);
     }
 
     /**
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ViewAndControls.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ViewAndControls.java
index 5c3d3d6817..090742a76d 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ViewAndControls.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/coverage/ViewAndControls.java
@@ -17,6 +17,7 @@
 package org.apache.sis.gui.coverage;
 
 import javafx.geometry.Insets;
+import javafx.geometry.Pos;
 import javafx.scene.Node;
 import javafx.scene.control.Control;
 import javafx.scene.control.Label;
@@ -31,7 +32,6 @@ import javafx.scene.layout.VBox;
 import javafx.scene.text.Font;
 import javafx.scene.text.FontWeight;
 import javafx.collections.ObservableList;
-import org.apache.sis.gui.internal.Styles;
 import org.apache.sis.storage.GridCoverageResource;
 import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
@@ -63,12 +63,6 @@ abstract class ViewAndControls {
      */
     private static final Insets NEXT_CAPTION_MARGIN = new Insets(30, 0, 6, 0);
 
-    /**
-     * Same indentation as {@link Styles#FORM_INSETS}, but without the space 
on other sides.
-     * This is used when the node is outside a group created by {@link 
Styles#createControlGrid(int, Label...)}.
-     */
-    static final Insets CONTENT_MARGIN = new Insets(0, 0, 0, 
Styles.FORM_INSETS.getLeft());
-
     /**
      * Index of {@link #sliceSelector} in the list of children of {@link 
#viewAndNavigation}.
      */
@@ -256,6 +250,8 @@ abstract class ViewAndControls {
     static Label labelOfGroup(final IndexedResourceBundle vocabulary, final 
short key, final Region group, final boolean isFirst) {
         final Label label = new Label(vocabulary.getString(key));
         label.setPadding(isFirst ? CAPTION_MARGIN : NEXT_CAPTION_MARGIN);
+        label.setMaxWidth(Double.MAX_VALUE);
+        label.setAlignment(Pos.CENTER);
         label.setLabelFor(group);
         label.setFont(fontOfGroup());
         return label;
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
index d7a52b71bd..0d2fd76e96 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.java
@@ -301,6 +301,11 @@ public class Resources extends IndexedResourceBundle {
          */
         public static final short Mercator = 44;
 
+        /**
+         * The image and the status bar use different reference systems.
+         */
+        public static final short MismatchedRS = 80;
+
         /**
          * New window
          */
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.properties
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.properties
index 3238bad540..8ca1f85558 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.properties
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources.properties
@@ -68,6 +68,7 @@ IsolinesInRange        = Generate isolines at constant 
interval\nstarting from g
 LicenseAgreement       = Do you accept the license shown below?
 Loading                = Loading\u2026
 Mercator               = Mercator
+MismatchedRS           = The image and the status bar use different reference 
systems.
 MainWindow             = Main window
 NewWindow              = New window
 NoFeatureTypeInfo      = No feature type information.
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources_fr.properties
 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources_fr.properties
index b4d044e410..189cb170cd 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources_fr.properties
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Resources_fr.properties
@@ -73,6 +73,7 @@ IsolinesInRange        = G\u00e9n\u00e8re des isolignes 
\u00e0 intervalle consta
 LicenseAgreement       = Acceptez-vous la licence ci-dessous?
 Loading                = Chargement\u2026
 Mercator               = Mercator
+MismatchedRS           = L\u2019image et la barre d\u2019\u00e9tat utilisent 
des syst\u00e8mes de r\u00e9f\u00e9rence diff\u00e9rents.
 MainWindow             = Fen\u00eatre principale
 NewWindow              = Nouvelle fen\u00eatre
 NoFeatureTypeInfo      = Pas d\u2019information sur le type d\u2019entit\u00e9.
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java
index b2255ec381..1cfe119d32 100644
--- 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java
+++ 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/internal/Styles.java
@@ -68,6 +68,12 @@ public final class Styles {
      */
     public static final Color SELECTED_TEXT = Color.WHITE;
 
+    /**
+     * Color of text for information purpose but which are not the main topic.
+     * This is using a lighter color for avoiding to be a source of 
distraction.
+     */
+    public static final Color FAINT_TEXT = Color.GRAY;
+
     /**
      * Color of the text saying that data are in process of being loaded.
      */
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapMenu.java 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapMenu.java
index dc9314100e..3c943cdbc7 100644
--- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapMenu.java
+++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/MapMenu.java
@@ -54,7 +54,7 @@ import org.apache.sis.referencing.IdentifiedObjects;
  * In current implementation, there is no mechanism for removing menu items.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.1
+ * @version 1.7
  * @since   1.1
  */
 public class MapMenu extends ContextMenu {
@@ -65,10 +65,20 @@ public class MapMenu extends ContextMenu {
 
     /**
      * A handler for controlling the contextual menu.
-     * Created when first needed.
+     * Created when first needed and should be considered final after 
initialization.
      */
     private MapCanvas.MenuHandler menuHandler;
 
+    /**
+     * Name of the currently selected reference system.
+     * This reference needs to be cached because JavaFX binds properties using 
weak references.
+     * If the users invoke {@link #selectedReferenceSystem()} only for 
registering listeners without
+     * storing the returned instance, their listeners will be lost unless we 
keep a reference here.
+     *
+     * @see #selectedReferenceSystem()
+     */
+    private ObservableObjectValue<String> selectedReferenceSystem;
+
     /**
      * Groups of menu items that have been added. Bits in this mask are set 
when {@link #addCopyOptions(StatusBar)}
      * {@link #addReferenceSystems(RecentReferenceSystems)} or similar methods 
are invoked. Each {@code addFoo(…)}
@@ -170,23 +180,22 @@ public class MapMenu extends ContextMenu {
         getItems().add(coordinates);
     }
 
-
     /**
-     * Returns an observable value for showing the currently selected CRS as a 
text.
+     * Returns an observable value for showing the name of the currently 
selected <abbr>CRS</abbr>.
      * The value is absent if {@link 
#addReferenceSystems(RecentReferenceSystems)} has never been invoked.
      *
-     * @return the currently selected CRS as a text.
+     * @return the currently selected <abbr>CRS</abbr> as a text.
      *
      * @see #addReferenceSystems(RecentReferenceSystems)
      */
     public Optional<ObservableObjectValue<String>> selectedReferenceSystem() {
-        if (menuHandler != null) {
+        if (selectedReferenceSystem == null && menuHandler != null) {
             final ObjectProperty<ReferenceSystem> selectedCrsProperty = 
menuHandler.selectedCrsProperty;
             if (selectedCrsProperty != null) {
-                return Optional.of(new SelectedCRS(selectedCrsProperty, 
canvas.getLocale()));
+                selectedReferenceSystem = new SelectedCRS(selectedCrsProperty, 
canvas.getLocale());
             }
         }
-        return Optional.empty();
+        return Optional.ofNullable(selectedReferenceSystem);
     }
 
     /**
diff --git 
a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java 
b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java
index 396b1a2eda..9ef30b67b7 100644
--- a/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java
+++ b/optional/src/org.apache.sis.gui/main/org/apache/sis/gui/map/StatusBar.java
@@ -49,6 +49,7 @@ import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ObjectPropertyBase;
 import javafx.beans.property.ReadOnlyObjectProperty;
 import javafx.beans.property.ReadOnlyObjectPropertyBase;
+import javafx.beans.property.ReadOnlyStringProperty;
 import javafx.beans.property.SimpleObjectProperty;
 import javafx.collections.ListChangeListener;
 import javafx.collections.ObservableList;
@@ -78,17 +79,17 @@ import org.apache.sis.coverage.grid.GridCoverage;
 import org.apache.sis.coverage.grid.GridGeometry;
 import org.apache.sis.coverage.grid.GridExtent;
 import org.apache.sis.portrayal.RenderException;
-import org.apache.sis.util.Classes;
 import org.apache.sis.util.ArraysExt;
 import org.apache.sis.util.Utilities;
 import org.apache.sis.util.Exceptions;
+import org.apache.sis.util.CharSequences;
 import org.apache.sis.util.ArgumentChecks;
 import org.apache.sis.util.ComparisonMode;
+import org.apache.sis.util.logging.Logging;
+import org.apache.sis.util.resources.Errors;
 import org.apache.sis.util.internal.shared.Strings;
 import org.apache.sis.measure.Quantities;
 import org.apache.sis.measure.Units;
-import org.apache.sis.util.resources.Errors;
-import org.apache.sis.util.logging.Logging;
 import org.apache.sis.gui.Widget;
 import org.apache.sis.gui.referencing.RecentReferenceSystems;
 import org.apache.sis.gui.internal.BackgroundThreads;
@@ -125,7 +126,7 @@ import org.opengis.coordinate.MismatchedDimensionException;
  * {@link #setLocalCoordinates(double, double)} explicitly instead.
  *
  * @author  Martin Desruisseaux (Geomatys)
- * @version 1.5
+ * @version 1.7
  * @since   1.1
  */
 public class StatusBar extends Widget implements EventHandler<MouseEvent> {
@@ -154,18 +155,33 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
 
     /**
      * The container of controls making the status bar.
-     * Contains {@link #message}, {@link #position} and {@link #sampleValues}.
+     * Contains {@link #message}, {@link #position} and {@link #sampleValues} 
in that order.
      *
      * @see #getView()
      */
     private final HBox view;
 
+    /**
+     * The default message to show when there is no error or operation in 
progress.
+     *
+     * @see #setDefaultMessage(String, Tooltip)
+     */
+    private String defaultMessage;
+
+    /**
+     * The tool tip to show on the default message, or {@code null} if none.
+     */
+    private Tooltip defaultMessageTeooltip;
+
     /**
      * Message to write in the middle of the status bar.
-     * This component usually has nothing to show; it is used mostly for error 
messages.
+     * This component can be used, for example, for error messages.
      * It takes all the space before {@link #position}.
      *
      * @see #getMessage()
+     * @see #setInfoMessage(String)
+     * @see #setErrorMessage(String, Throwable)
+     * @see #setDefaultMessage(String, Tooltip)
      */
     private final Label message;
 
@@ -270,6 +286,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * directly instead. We omit the "Property" suffix for making this 
operation more natural.
      *
      * @see #position
+     * @see #positionReferenceSystemName()
      */
     public final ReadOnlyObjectProperty<ReferenceSystem> 
positionReferenceSystem;
 
@@ -408,6 +425,15 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      */
     protected final Label position;
 
+    /**
+     * Tool tip of {@link #position} showing the name of the reference system 
of the coordinates.
+     * The text property of this tool tip may be used outside this class.
+     *
+     * @see #positionReferenceSystem
+     * @see #positionReferenceSystemName()
+     */
+    private final Tooltip positionReferenceSystemName;
+
     /**
      * Maximal length of {@linkplain #position} text found so far. This is 
used for detecting when
      * to compute a minimal {@linkplain #position} width for making sure that 
the coordinates stay
@@ -502,6 +528,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         position = new Label();
         position.setAlignment(Pos.CENTER_RIGHT);
         position.setTextAlignment(TextAlignment.RIGHT);
+        positionReferenceSystemName = new Tooltip();
 
         view = new HBox(6, message, position);
         view.setPadding(PADDING);
@@ -1057,7 +1084,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         targetCoordinates = target;         // Assign only after above succeed.
         formatAsIdentifiers = null;
         format.setGroundAccuracy(Quantities.max(accuracy, 
lowestAccuracy.get()));
-        setTooltip(crs);
+        setCoordinateTooltip(crs);
         /*
          * Prepare the text to show when the mouse is outside the canvas area.
          * We will write axis abbreviations, for example "(φ, λ)".
@@ -1271,7 +1298,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         fullOperationSearchRequired = null;
         outsideText = null;
         setErrorMessage(null, null);
-        setTooltip(coder.getReferenceSystem());
+        setCoordinateTooltip(coder.getReferenceSystem());
         position.setText(identifier);
         ((PositionSystem) positionReferenceSystem).fireValueChangedEvent();
         rewritePosition(current);
@@ -1570,18 +1597,23 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     /**
      * Sets the tooltip text to show when the mouse cursor is over the 
coordinate values.
      */
-    private void setTooltip(final ReferenceSystem crs) {
-        String text = IdentifiedObjects.getDisplayName(crs, getLocale());
-        Tooltip tp = null;
-        if (text != null) {
-            tp = position.getTooltip();
-            if (tp == null) {
-                tp = new Tooltip(text);
-            } else {
-                tp.setText(text);
-            }
-        }
-        position.setTooltip(tp);
+    private void setCoordinateTooltip(final ReferenceSystem crs) {
+        final String text = IdentifiedObjects.getDisplayName(crs, getLocale());
+        positionReferenceSystemName.setText(text);
+        position.setTooltip(text != null ? positionReferenceSystemName : null);
+    }
+
+    /**
+     * Returns a property for the name of the reference system used by the 
coordinates.
+     * This is the localized name of the {@link #positionReferenceSystem} 
property.
+     *
+     * @return a property for the name of the reference system used by the 
coordinates.
+     *
+     * @see #positionReferenceSystem
+     * @since 1.7
+     */
+    public ReadOnlyStringProperty positionReferenceSystemName() {
+        return positionReferenceSystemName.textProperty();
     }
 
     /**
@@ -1589,12 +1621,58 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      *
      * @return the current message, or an empty value if none.
      *
+     * @see #setInfoMessage(String)
+     * @see #setErrorMessage(String, Throwable)
+     * @see #setDefaultMessage(String, Tooltip)
+     *
      * @since 1.3
      */
     public Optional<String> getMessage() {
         return Optional.ofNullable(message.getText());
     }
 
+    /**
+     * Sets the message, or restore the default message if the given text is 
null.
+     *
+     * @param text      the text, or {@code null} if none.
+     * @param textFill  the color to use for the text.
+     */
+    private void setMessage(String text, Color textFill) {
+        Tooltip tooltip = null;
+        if (text == null) {
+            text     = defaultMessage;
+            textFill = Styles.FAINT_TEXT;
+            tooltip  = defaultMessageTeooltip;
+        }
+        message.setVisible(text != null);
+        message.setText(text);
+        message.setTextFill(textFill);
+        message.setTooltip(tooltip);
+    }
+
+    /**
+     * Sets the default message to show when there is no error or operation in 
progress.
+     * If {@link #setInfoMessage(String)} or {@link #setErrorMessage(String, 
Throwable)}
+     * is invoked, the informative or error message has precedence over this 
default message.
+     * When the informative or error message is reset to {@code null}, the 
default message is restored.
+     *
+     * @param  text     message to show when there is no other kind of 
message, or {@code null} if none.
+     * @param  tooltip  optional tool tip to show on the default message, or 
{@code null} if none.
+     *
+     * @since 1.7
+     */
+    public void setDefaultMessage(String text, final Tooltip tooltip) {
+        text = Strings.trimOrNull(text);
+        defaultMessage = text;
+        defaultMessageTeooltip = tooltip;
+        // Use the text fill as a way to identify that the message is not an 
error or information.
+        if (message.getText() == null || message.getTextFill() == 
Styles.FAINT_TEXT) {
+            message.setTextFill(Styles.FAINT_TEXT);
+            message.setText(text);
+            message.setTooltip(tooltip);
+        }
+    }
+
     /**
      * Shows or hides an informative message on the status bar.
      * The message should be temporary, for example for telling that a loading 
is in progress.
@@ -1604,11 +1682,8 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
      * @since 1.3
      */
     public void setInfoMessage(String text) {
-        text = Strings.trimOrNull(text);
-        message.setVisible(text != null);
+        setMessage(Strings.trimOrNull(text), Styles.LOADING_TEXT);
         message.setGraphic(null);
-        message.setText(text);
-        message.setTextFill(Styles.LOADING_TEXT);
     }
 
     /**
@@ -1624,15 +1699,16 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         text = Strings.trimOrNull(text);
         Button more = null;
         if (details != null) {
-            final String alert = (text != null) ? text : cause(details);
+            if (text == null) {
+                text = cause(details);
+            }
+            final String alert = text;  // Because lambda functions want final 
variable.
             more = new Button(Styles.ERROR_DETAILS_ICON);
             more.setOnAction((e) -> ExceptionReporter.show(getView(),
                     
Resources.forLocale(getLocale()).getString(Resources.Keys.ErrorDetails), alert, 
details));
         }
-        message.setVisible(text != null);
+        setMessage(text, Styles.ERROR_TEXT);
         message.setGraphic(more);
-        message.setText(text);
-        message.setTextFill(Styles.ERROR_TEXT);
     }
 
     /**
@@ -1669,7 +1745,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
     /**
      * Returns a string representation of the message of the given exception.
      * If the exception is a wrapper, the exception cause is taken.
-     * If there is no message, the exception class name is returned.
+     * If there is no message, the exception class name is used.
      *
      * @param  e  the exception.
      * @return the exception message or class name.
@@ -1680,7 +1756,7 @@ public class StatusBar extends Widget implements 
EventHandler<MouseEvent> {
         }
         String text = Exceptions.getLocalizedMessage(e, getLocale());
         if (text == null) {
-            text = Classes.getShortClassName(e);
+            text = 
CharSequences.camelCaseToWords(e.getClass().getSimpleName(), false).toString();
         }
         return text;
     }


Reply via email to