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 48f93998b67544f648852c52a7cd2d0e0313daed
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Mon Jan 27 14:15:20 2020 +0100

    Fix layout of error message.
---
 .../org/apache/sis/gui/coverage/GridError.java     | 76 ++++++++++++++++------
 .../java/org/apache/sis/gui/coverage/GridRow.java  |  1 +
 .../java/org/apache/sis/gui/coverage/GridTile.java | 39 +++++++----
 .../org/apache/sis/gui/coverage/GridViewSkin.java  | 66 ++++++++++++-------
 .../apache/sis/internal/gui/ExceptionReporter.java | 44 ++++++++-----
 .../org/apache/sis/internal/gui/Resources.java     |  5 ++
 .../apache/sis/internal/gui/Resources.properties   |  1 +
 .../sis/internal/gui/Resources_fr.properties       |  1 +
 .../org/apache/sis/util/resources/Vocabulary.java  |  5 ++
 .../sis/util/resources/Vocabulary.properties       |  1 +
 .../sis/util/resources/Vocabulary_fr.properties    |  1 +
 11 files changed, 167 insertions(+), 73 deletions(-)

diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java
index 269a89d..335c2ce 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java
@@ -17,9 +17,16 @@
 package org.apache.sis.gui.coverage;
 
 import java.awt.Rectangle;
+import javafx.geometry.Pos;
+import javafx.event.ActionEvent;
+import javafx.scene.control.Button;
 import javafx.scene.control.Label;
+import javafx.scene.layout.TilePane;
+import javafx.scene.layout.VBox;
 import javafx.scene.paint.Color;
-import org.apache.sis.util.Classes;
+import org.apache.sis.internal.gui.ExceptionReporter;
+import org.apache.sis.internal.gui.Resources;
+import org.apache.sis.util.resources.Vocabulary;
 
 
 /**
@@ -30,7 +37,12 @@ import org.apache.sis.util.Classes;
  * @since   1.1
  * @module
  */
-final class GridError extends Label {
+final class GridError extends VBox {
+    /**
+     * The area where to write the error message.
+     */
+    private final Label message;
+
     /**
      * If we failed to fetch a tile, the tile in error. If more than one tile 
has an error,
      * then the tile having the largest intersection with the view area. This 
visible error
@@ -39,6 +51,12 @@ final class GridError extends Label {
     private GridTile.Error visibleError;
 
     /**
+     * The last error reported. This is usually equal to {@link 
#visibleError}, except that
+     * the visible error may be {@code null} while {@code lastError} is never 
reset to null.
+     */
+    private GridTile.Error lastError;
+
+    /**
      * The zero-based row and columns indices of the area currently shown in 
{@link GridView}.
      * This is updated by {@link GridViewSkin#layoutChildren(double, double, 
double, double)}.
      */
@@ -57,7 +75,21 @@ final class GridError extends Label {
      * Creates a new error control.
      */
     GridError() {
-        setTextFill(Color.RED);
+        super(9);
+        message = new Label();
+        message.setTextFill(Color.RED);
+        final Button   details = new 
Button(Vocabulary.format(Vocabulary.Keys.Details));
+        final Button   retry   = new 
Button(Vocabulary.format(Vocabulary.Keys.Retry));
+        final TilePane buttons = new TilePane(12, 0, details, retry);
+        message.setLabelFor(buttons);
+        buttons.setPrefRows(1);
+        buttons.setPrefColumns(2);
+        buttons.setAlignment(Pos.CENTER);
+        details.setMaxWidth(100);           // Arbitrary limit, width enough 
for allowing TilePane to resize.
+        retry  .setMaxWidth(100);
+        getChildren().addAll(message, buttons);
+        setAlignment(Pos.CENTER);
+        details.setOnAction(this::showDetails);
     }
 
     /**
@@ -66,38 +98,44 @@ final class GridError extends Label {
      * @param  area  zero-based row and columns indices of the area currently 
shown in {@link GridView}.
      */
     final void initialize(final Rectangle area) {
-        setVisible(false);
         viewArea     = area;
         visibleError = null;
-        updateCount++;
+        if (++updateCount == 0) {
+            updateCount = 1;        // Paranoiac safety in case we did a cycle 
over all integer values.
+        }
     }
 
     /**
      * Updates this error control with the given status. If this control is 
already showing an error message,
-     * it will be updated only if the given status cover a larger view area.
+     * it will be updated only if the given status cover a larger view area. 
If this control has been updated,
+     * then this method returns the zero-based row and column indices of the 
region in error in the view area.
+     * Otherwise (if this method did nothing), this method returns {@code 
null}.
      *
      * @param  status  the candidate error status.
+     * @return new indices of visible area, or {@code null} if no change.
+     *         This is a direct reference to internal field; do not modify.
      */
-    final boolean update(final GridTile.Error status) {
+    final Rectangle update(final GridTile.Error status) {
         if (status != visibleError && status.updateAndCompare(updateCount, 
viewArea, visibleError)) {
-            visibleError = status;
+            visibleError = lastError = status;
+            String text = status.message;
             final Throwable exception = status.exception;
-            String message = exception.getLocalizedMessage();
-            if (message == null) {
-                message = Classes.getShortClassName(exception);
+            String more = exception.getLocalizedMessage();
+            if (more != null) {
+                text = text + System.lineSeparator() + more;
             }
-            setText(message);
-            setVisible(true);
-            return true;
+            message.setText(text);
+            return status.visibleArea;
         }
-        return false;
+        return null;
     }
 
     /**
-     * Returns the zero-based row and column indices of the region in error in 
the view area.
-     * This method returns a direct reference to internal instance; do not 
modify.
+     * Invoked when the user click on the "details" button.
      */
-    final Rectangle getVisibleArea() {
-        return visibleError.visibleArea;
+    private void showDetails(final ActionEvent event) {
+        if (lastError != null) {
+            
ExceptionReporter.show(Resources.format(Resources.Keys.ErrorDataAccess), 
lastError.message, lastError.exception);
+        }
     }
 }
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java
index 56b8bf2..721d2aa 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridRow.java
@@ -82,6 +82,7 @@ final class GridRow extends IndexedCell<Void> {
         if (skin != null) {
             ((GridRowSkin) skin).setRowIndex(row);
         }
+        updateItem(null, row < 0 || row >= view.getImageHeight());
     }
 
     /**
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java
index 32b3110..a1fa654 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridTile.java
@@ -21,6 +21,7 @@ import java.awt.image.Raster;
 import java.awt.image.RenderedImage;
 import javafx.concurrent.Task;
 import org.apache.sis.internal.gui.BackgroundThreads;
+import org.apache.sis.internal.gui.Resources;
 
 
 /**
@@ -148,7 +149,8 @@ final class GridTile {
                     tile   = null;
                     status = null;
                     if (view.getImage() == image) {
-                        status = new Error(view.getTileBounds(tileX, tileY), 
getException());
+                        status = new 
Error(Resources.format(Resources.Keys.CanNotFetchTile_2, tileX, tileY),
+                                           view.getTileBounds(tileX, tileY), 
getException());
                         view.contentChanged(false);     // For rendering the 
error message.
                     }
                 }
@@ -185,7 +187,12 @@ final class GridTile {
          * in the sense that {@link #load(GridView)} returns {@code null} 
immediately, except that no
          * error message is recorded.
          */
-        private static final Error LOADING = new Error(null, null);
+        private static final Error LOADING = new Error(null, null, null);
+
+        /**
+         * The error message saying "can not fetch tile (x, y)", with tile 
indices.
+         */
+        final String message;
 
         /**
          * If we failed to load the tile, the reason for the failure.
@@ -201,8 +208,6 @@ final class GridTile {
         /**
          * Intersection of {@link #region} with the area currently shown in 
the view.
          * May vary with scrolling and is empty if the tile in error is 
outside visible area.
-         *
-         * @see GridError#getVisibleArea()
          */
         Rectangle visibleArea;
 
@@ -215,19 +220,13 @@ final class GridTile {
         /**
          * Creates an error status with the given cause.
          */
-        private Error(final Rectangle region, final Throwable exception) {
+        private Error(final String message, final Rectangle region, final 
Throwable exception) {
+            this.message   = message;
             this.region    = region;
             this.exception = exception;
         }
 
         /**
-         * Returns the area inside {@link #visibleArea}.
-         */
-        private long area() {
-            return visibleArea.width * (long) visibleArea.height;
-        }
-
-        /**
          * Recomputes the {@link #visibleArea} value if needed, then returns 
{@code true}
          * if the visible area in this {@code Error} if wider than the one in 
{@code other}.
          *
@@ -241,7 +240,21 @@ final class GridTile {
                 visibleArea = viewArea.intersection(region);
                 updateCount = stamp;
             }
-            return (other == null) || area() > other.area();
+            if (other == null || other.visibleArea.isEmpty()) {
+                return true;
+            }
+            if (visibleArea.isEmpty()) {
+                return false;
+            }
+            /*
+             * Gives precedence to width instead than computing the area 
because the error
+             * messsage will be written horizontally, so we want more space 
for writing it.
+             */
+            int c = Integer.compare(visibleArea.width, 
other.visibleArea.width);
+            if (c == 0) {
+                c = Integer.compare(visibleArea.height, 
other.visibleArea.height);
+            }
+            return c > 0;
         }
     }
 }
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
index b3fb8f9..53d65bb 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridViewSkin.java
@@ -179,16 +179,30 @@ final class GridViewSkin extends 
VirtualContainerBase<GridView, GridRow> {
         if (error == null) {
             error = new GridError();
             getChildren().add(error);
+            computeErrorBounds((Flow) getVirtualFlow());
         }
-        if (error.update(status)) {
-            final double cellHeight = getVirtualFlow().getFixedCellSize();
-            final java.awt.Rectangle area = error.getVisibleArea();
-            positionInArea​(error,
-                    area.x      * cellWidth  + headerWidth,
-                    area.y      * cellHeight + topBackground.getHeight(),
-                    area.width  * cellWidth,
-                    area.height * cellHeight,
-                    0, HPos.CENTER, VPos.CENTER);
+        final java.awt.Rectangle area = error.update(status);
+        if (area != null) {
+            final Flow flow = (Flow) getVirtualFlow();
+            final GridRow firstVisibleRow = area.isEmpty() ? null : 
flow.getFirstVisibleCell();
+            error.setVisible(firstVisibleRow != null);
+            if (firstVisibleRow != null) {
+                final double cellHeight = flow.getFixedCellSize();
+                final double width  = area.width  * cellWidth;
+                final double height = area.height * cellHeight;
+                layoutInArea​(error,
+                        cellWidth  * (area.x - firstVisibleColumn)         + 
leftBackground.getWidth(),
+                        cellHeight * (area.y - firstVisibleRow.getIndex()) + 
topBackground.getHeight(),
+                        width, height, Node.BASELINE_OFFSET_SAME_AS_HEIGHT, 
HPos.CENTER, VPos.CENTER);
+                /*
+                 * If after layout the error message size appears too large 
for the remaining space, hide it.
+                 * The intent is to avoid having the message and buttons on 
top of numbers in valid tiles.
+                 * It does not seem to work fully however (some overlaps can 
still happen).
+                 */
+                if (error.getHeight() > height || error.getWidth() > width) {
+                    error.setVisible(false);
+                }
+            }
         }
     }
 
@@ -395,25 +409,29 @@ final class GridViewSkin extends 
VirtualContainerBase<GridView, GridRow> {
             int column = firstVisibleColumn;
             for (final Node cell : headerRow) {
                 ((GridCell) cell).setText(view.formatHeaderValue(column++, 
false));
-                layoutInArea(cell, pos, y, cellWidth, headerHeight, 0, 
HPos.CENTER, VPos.CENTER);
+                positionInArea(cell, pos, y, cellWidth, headerHeight, 
Node.BASELINE_OFFSET_SAME_AS_HEIGHT, HPos.CENTER, VPos.CENTER);
                 pos += cellWidth;
             }
         }
-        /*
-         * If an error exists somewhere, computes as estimation of the visible 
region
-         * as zero-based column and row indices. We use an AWT rectangle 
instead than
-         * JavaFX object because this rectangle will be intersected with AWT 
rectangle.
-         */
         if (error != null) {
-            final java.awt.Rectangle viewArea = new java.awt.Rectangle();
-            final GridRow firstVisibleRow = flow.getFirstVisibleCell();
-            if (firstVisibleRow != null) {
-                viewArea.x      = firstVisibleColumn;
-                viewArea.y      = firstVisibleRow.getIndex();
-                viewArea.width  = (int) ((flow.getWidth() - headerWidth) / 
cellWidth);
-                viewArea.height = (int) (flow.getVisibleHeight() / 
flow.getFixedCellSize());
-            }
-            error.initialize(viewArea);
+            computeErrorBounds(flow);
+        }
+    }
+
+    /**
+     * If an error exists somewhere, computes as estimation of the visible 
region
+     * as zero-based column and row indices. We use an AWT rectangle instead 
than
+     * JavaFX object because this rectangle will be intersected with AWT 
rectangle.
+     */
+    private void computeErrorBounds(final Flow flow) {
+        final java.awt.Rectangle viewArea = new java.awt.Rectangle();
+        final GridRow firstVisibleRow = flow.getFirstVisibleCell();
+        if (firstVisibleRow != null) {
+            viewArea.x      = firstVisibleColumn;
+            viewArea.y      = firstVisibleRow.getIndex();
+            viewArea.width  = (int) ((flow.getWidth() - 
leftBackground.getWidth()) / cellWidth);
+            viewArea.height = (int) (flow.getVisibleHeight() / 
flow.getFixedCellSize());
         }
+        error.initialize(viewArea);
     }
 }
diff --git 
a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
 
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
index 1c5a143..b11d1ca 100644
--- 
a/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
+++ 
b/application/sis-javafx/src/main/java/org/apache/sis/internal/gui/ExceptionReporter.java
@@ -167,30 +167,40 @@ public final class ExceptionReporter implements 
EventHandler<ActionEvent> {
      * @param title       {@link Resources.Keys} of the title, or 0 if unknown.
      * @param text        {@link Resources.Keys} of the text (possibly with 
arguments), or 0 if unknown.
      * @param arguments   the arguments for creating the text identified by 
the {@code text} key.
-     * @param exception   the exception to report.
+     * @param exception   the exception to report, or {@code null} if none.
      */
     private static void show(final short title, final short text, final 
Object[] arguments, final Throwable exception) {
         if (exception != null) {
-            String message = exception.getLocalizedMessage();
-            if (message == null) {
-                message = Classes.getShortClassName(exception);
-            }
-            final Alert alert = new Alert(Alert.AlertType.ERROR);
+            String t = null, h = null;
             if ((title | text) != 0) {
                 final Resources resources = Resources.getInstance();
-                if (title != 0) {
-                    alert.setTitle(resources.getString(title));
-                }
-                if (text != 0) {
-                    alert.setHeaderText(resources.getString(text, arguments));
-                }
+                if (title != 0) t = resources.getString(title);
+                if (text  != 0) h = resources.getString(text, arguments);
             }
-            alert.setContentText(message);
-            final DialogPane pane = alert.getDialogPane();
-            pane.setExpandableContent(new ExceptionReporter(exception).trace);
-            pane.setPrefWidth(650);
-            alert.show();
+            show(t, h, exception);
+        }
+    }
+
+    /**
+     * Constructs and shows the exception reporter.
+     *
+     * @param title      the window the title, or {@code null} if none.
+     * @param text       the text in the dialog box, or {@code null} if none.
+     * @param exception  the exception to report.
+     */
+    public static void show(final String title, final String text, final 
Throwable exception) {
+        String message = exception.getLocalizedMessage();
+        if (message == null) {
+            message = Classes.getShortClassName(exception);
         }
+        final Alert alert = new Alert(Alert.AlertType.ERROR);
+        if (title != null) alert.setTitle(title);
+        if (text  != null) alert.setHeaderText(text);
+        alert.setContentText(message);
+        final DialogPane pane = alert.getDialogPane();
+        pane.setExpandableContent(new ExceptionReporter(exception).trace);
+        pane.setPrefWidth(650);
+        alert.show();
     }
 
     /**
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 7f92874..c864ce9 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
@@ -81,6 +81,11 @@ public final class Resources extends IndexedResourceBundle {
         public static final short CanNotCreateCRS_1 = 35;
 
         /**
+         * Can not fetch tile ({0}, {1}).
+         */
+        public static final short CanNotFetchTile_2 = 45;
+
+        /**
          * Can not open “{0}”.
          */
         public static final short CanNotReadFile_1 = 5;
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 e8636a7..db5d34f 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
@@ -22,6 +22,7 @@
 
 Abstract               = Abstract:
 AllFiles               = All files
+CanNotFetchTile_2      = Can not fetch tile ({0}, {1}).
 CanNotReadFile_1       = Can not open \u201c{0}\u201d.
 CanNotClose_1          = Can not close \u201c{0}\u201d. Data may be lost.
 CanNotCreateCRS_1      = Can not create reference system \u201c{0}\u201d.
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 c8e25a5..2699e37 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
@@ -27,6 +27,7 @@
 
 Abstract               = R\u00e9sum\u00e9\u00a0:
 AllFiles               = Tous les fichiers
+CanNotFetchTile_2      = Ne peut pas obtenir la tuile ({0}, {1}).
 CanNotReadFile_1       = Ne peut pas ouvrir \u00ab\u202f{0}\u202f\u00bb.
 CanNotClose_1          = Ne peut pas fermer \u00ab\u202f{0}\u202f\u00bb. Il 
pourrait y avoir une perte de donn\u00e9es.
 CanNotCreateCRS_1      = Ne peut pas cr\u00e9er le syst\u00e8me de 
r\u00e9f\u00e9rence \u00ab\u202f{0}\u202f\u00bb.
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
index 6307ba9..94f8285 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.java
@@ -770,6 +770,11 @@ public final class Vocabulary extends 
IndexedResourceBundle {
         public static final short Result = 132;
 
         /**
+         * Retry
+         */
+        public static final short Retry = 189;
+
+        /**
          * Root
          */
         public static final short Root = 133;
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
index aecdb5b..6d2e80a 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary.properties
@@ -157,6 +157,7 @@ RemoteConfiguration     = Remote configuration
 RepresentativeValue     = Representative value
 Resolution              = Resolution
 Result                  = Result
+Retry                   = Retry
 Root                    = Root
 RootMeanSquare          = Root Mean Square
 SampleDimensions        = Sample dimensions
diff --git 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
index 0276620..bc1779e 100644
--- 
a/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
+++ 
b/core/sis-utility/src/main/java/org/apache/sis/util/resources/Vocabulary_fr.properties
@@ -164,6 +164,7 @@ RemoteConfiguration     = Configuration distante
 RepresentativeValue     = Valeur repr\u00e9sentative
 Resolution              = R\u00e9solution
 Result                  = R\u00e9sultat
+Retry                   = R\u00e9essayer
 Root                    = Racine
 RootMeanSquare          = Moyenne quadratique
 SampleDimensions        = Dimensions d\u2019\u00e9chantillonnage

Reply via email to