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 7fa7573 Replaced the `loading` flag by `status` with more advanced
cabability to report error.
7fa7573 is described below
commit 7fa75738b61e8e8878a0ea89e20b3654d71fac29
Author: Martin Desruisseaux <[email protected]>
AuthorDate: Sun Jan 26 00:54:15 2020 +0100
Replaced the `loading` flag by `status` with more advanced cabability to
report error.
---
.../org/apache/sis/gui/coverage/GridError.java | 103 +++++++++++
.../java/org/apache/sis/gui/coverage/GridTile.java | 202 ++++++++++++++++-----
.../java/org/apache/sis/gui/coverage/GridView.java | 158 ++++++++++------
.../org/apache/sis/gui/coverage/GridViewSkin.java | 54 +++++-
4 files changed, 406 insertions(+), 111 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
new file mode 100644
index 0000000..269a89d
--- /dev/null
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridError.java
@@ -0,0 +1,103 @@
+/*
+ * 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.coverage;
+
+import java.awt.Rectangle;
+import javafx.scene.control.Label;
+import javafx.scene.paint.Color;
+import org.apache.sis.util.Classes;
+
+
+/**
+ * Controls to put in the middle of a tile if an error occurred while loading
that tile.
+ *
+ * @author Martin Desruisseaux (Geomatys)
+ * @version 1.1
+ * @since 1.1
+ * @module
+ */
+final class GridError extends Label {
+ /**
+ * 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
+ * may change during scrolling.
+ */
+ private GridTile.Error visibleError;
+
+ /**
+ * 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)}.
+ */
+ private Rectangle viewArea;
+
+ /**
+ * Incremented every time that a new layout is performed. This is used for
detecting if a
+ * {@link GridTile.Error} instance should recompute its visible area. It
is not a problem
+ * if this value overflows; we just check if values differ, not which one
is greater.
+ *
+ * @see GridTile.Error#updateCount
+ */
+ private int updateCount;
+
+ /**
+ * Creates a new error control.
+ */
+ GridError() {
+ setTextFill(Color.RED);
+ }
+
+ /**
+ * Invoked by {@link GridViewSkin#layoutChildren(double, double, double,
double)} when a new layout is beginning.
+ *
+ * @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++;
+ }
+
+ /**
+ * 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.
+ *
+ * @param status the candidate error status.
+ */
+ final boolean update(final GridTile.Error status) {
+ if (status != visibleError && status.updateAndCompare(updateCount,
viewArea, visibleError)) {
+ visibleError = status;
+ final Throwable exception = status.exception;
+ String message = exception.getLocalizedMessage();
+ if (message == null) {
+ message = Classes.getShortClassName(exception);
+ }
+ setText(message);
+ setVisible(true);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * 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.
+ */
+ final Rectangle getVisibleArea() {
+ return visibleError.visibleArea;
+ }
+}
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 e9e2a4e..32b3110 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
@@ -16,6 +16,7 @@
*/
package org.apache.sis.gui.coverage;
+import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import javafx.concurrent.Task;
@@ -37,25 +38,25 @@ final class GridTile {
final int tileX, tileY;
/**
- * Hash code value computed from tile indices.
+ * Hash code value computed from tile indices only. Other fields must be
ignored.
+ *
+ * @see #hashCode()
*/
private final int hashCode;
/**
* The tile, or {@code null} if not yet loaded.
+ *
+ * @see #tile()
*/
- Raster tile;
-
- /**
- * Non-null if an error occurred while reading the tile.
- */
- Throwable error;
+ private Raster tile;
/**
- * Whether a tile loading is in progress. Used for avoiding to create many
threads requesting the same tile.
- * If loading is in progress, other requests for that tile will return
{@code null} immediately.
+ * Non-null if a loading is in progress or if an error occurred while
fetching the tile.
+ * The loading status is used for avoiding to create many threads
requesting the same tile.
+ * If loading is in progress, requests for that tile will return {@code
null} immediately.
*/
- private boolean loading;
+ private Error status;
/**
* Creates a new tile for the given tile coordinates.
@@ -67,14 +68,55 @@ final class GridTile {
}
/**
+ * Returns a hash code value for this tile. This hash code value must be
based only on tile indices;
+ * the {@link #tile} and the {@link #status} must be ignored, because we
will use {@link GridTile}
+ * instances also as keys for locating tiles in a hash map.
+ */
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ /**
+ * Compares the indices of this tile with the given object for equality.
+ * Only indices are compared; the raster is ignored. See {@link
#hashCode()} for more information.
+ */
+ @Override
+ public boolean equals(final Object other) {
+ if (other instanceof GridTile) {
+ final GridTile that = (GridTile) other;
+ return tileX == that.tileX && tileY == that.tileY;
+ // Intentionally no other comparisons.
+ }
+ return false;
+ }
+
+ /**
+ * Returns a string representation for debugging purpose only.
+ */
+ @Override
+ public String toString() {
+ return getClass().getCanonicalName() + '[' + tileX + ", " + tileY +
']';
+ }
+
+ /**
+ * Returns the cached tile if available, or {@code null} otherwise.
+ * If null, then the caller should invoke {@link #load(GridView)}.
+ */
+ final Raster tile() {
+ return tile;
+ }
+
+ /**
* Loads tile from the given image in a background thread and informs the
specified view
- * when the tile become available.
+ * when the tile become available. If we already failed to load that tile
in a previous
+ * attempt, then this method may set the {@link GridViewSkin#error} field.
*
* @param view the view for which to load a tile.
*/
final void load(final GridView view) {
- if (!loading && error == null) {
- loading = true;
+ if (status == null) {
+ status = Error.LOADING; //
Pseudo-error.
final RenderedImage image = view.getImage();
BackgroundThreads.execute(new Task<Raster>() {
/** Invoked in background thread for fetching the tile. */
@@ -82,60 +124,124 @@ final class GridTile {
return image.getTile(tileX, tileY);
}
- /** Invoked in JavaFX thread on success. */
+ /**
+ * Invoked in JavaFX thread on success. If {@link GridView} is
still showing
+ * the same image, it will be informed that the tile is
available. Otherwise
+ * (if the image has changed) we ignore the result.
+ */
@Override protected void succeeded() {
super.succeeded();
- tile = getValue();
- error = null;
- loading = false;
- view.contentChanged(false);
+ tile = null;
+ status = null;
+ if (view.getImage() == image) {
+ tile = getValue();
+ view.contentChanged(false);
+ }
}
- /** Invoked in JavaFX thread on failure. */
+ /**
+ * Invoked in JavaFX thread on failure. Discards everything
and sets the error message
+ * if {@link GridView} is still showing the image for which we
failed to load a tile.
+ */
@Override protected void failed() {
super.failed();
- tile = null;
- error = getException();
- loading = false;
+ tile = null;
+ status = null;
+ if (view.getImage() == image) {
+ status = new Error(view.getTileBounds(tileX, tileY),
getException());
+ view.contentChanged(false); // For rendering the
error message.
+ }
}
- /** Invoked in JavaFX thread on cancellation. */
+ /**
+ * Invoked in JavaFX thread on cancellation. Just discard
everything.
+ * Ideally we should interrupt the {@link
RenderedImage#getTile(int, int)}
+ * process, but we currently have no API for that.
+ */
@Override protected void cancelled() {
super.cancelled();
- tile = null;
- error = null;
- loading = false;
+ tile = null;
+ status = null;
}
});
+ } else if (status != Error.LOADING) {
+ /*
+ * A previous attempt failed to load that tile. We may have an
error message to report.
+ * If more than one tile failed, take the one with largest visible
area.
+ */
+ ((GridViewSkin) view.getSkin()).errorOccurred(status);
}
}
/**
- * Returns a hash code value for this tile.
+ * The status of a tile request, either {@link #LOADING} or any other
instance in case of error.
+ * If not {@link #LOADING}, this class contains the reason why a tile
request failed, together
+ * with some information that depends on the viewing context. In
particular {@link #visibleArea}
+ * needs to be recomputed every time the viewed area in the {@link
GridView} changed.
*/
- @Override
- public int hashCode() {
- return hashCode;
- }
+ static final class Error {
+ /**
+ * A pseudo-error for saying that the tile is being fetched. Its
effect is similar to an error
+ * 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);
- /**
- * Compares the indices of this tile with the given object for equality.
- * Only indices are compared; the raster is ignored.
- */
- @Override
- public boolean equals(final Object other) {
- if (other instanceof GridTile) {
- final GridTile that = (GridTile) other;
- return tileX == that.tileX && tileY == that.tileY;
+ /**
+ * If we failed to load the tile, the reason for the failure.
+ */
+ final Throwable exception;
+
+ /**
+ * If we failed to load the tile, the zero-based row and column
indices of the tile.
+ * This is computed by {@link GridView#getTileBounds(int, int)} and
should be constant.
+ */
+ private final Rectangle region;
+
+ /**
+ * 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;
+
+ /**
+ * The {@link GridError#updateCount} value last time that {@link
#visibleArea} was computed.
+ * Used for detecting if we should recompute the visible area.
+ */
+ private int updateCount;
+
+ /**
+ * Creates an error status with the given cause.
+ */
+ private Error(final Rectangle region, final Throwable exception) {
+ this.region = region;
+ this.exception = exception;
}
- return false;
- }
- /**
- * Returns a string representation for debugging purpose only.
- */
- @Override
- public String toString() {
- return getClass().getCanonicalName() + '[' + tileX + ", " + tileY +
']';
+ /**
+ * 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}.
+ *
+ * @param stamp value of {@link GridError#updateCount}.
+ * @param viewArea value of {@link GridError#viewArea}.
+ * @param other the previous error, or {@code null}.
+ * @return whether this status should replace {@code other}.
+ */
+ final boolean updateAndCompare(final int stamp, final Rectangle
viewArea, final Error other) {
+ if (updateCount != stamp) {
+ visibleArea = viewArea.intersection(region);
+ updateCount = stamp;
+ }
+ return (other == null) || area() > other.area();
+ }
}
}
diff --git
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
index ee5c8ca..3e330fe 100644
---
a/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
+++
b/application/sis-javafx/src/main/java/org/apache/sis/gui/coverage/GridView.java
@@ -19,6 +19,7 @@ package org.apache.sis.gui.coverage;
import java.util.Map;
import java.text.NumberFormat;
import java.text.FieldPosition;
+import java.awt.Rectangle;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.SampleModel;
@@ -82,7 +83,13 @@ public class GridView extends Control {
/**
* Information copied from {@link #imageProperty} for performance.
*/
- private int width, height, minX, minY, tileWidth, tileHeight, numXTiles;
+ private int width, height, minX, minY, numXTiles;
+
+ /**
+ * Information copied from {@link #imageProperty} for performance.
+ * Must be always greater than zero for avoiding division by zero.
+ */
+ private int tileWidth, tileHeight;
/**
* Information copied and adjusted from {@link #imageProperty} for
performance. Values are adjusted for using
@@ -195,9 +202,11 @@ public class GridView extends Control {
private String lastValueAsText;
/**
- * Whether the sample values are integers.
+ * Whether the sample values are integers. We use this flag for deciding
which {@code Raster.getSampleValue(…)}
+ * method to invoke, which {@code NumberFormat.format(…)} method to
invoke, and whether to set a format pattern
+ * with fraction digits.
*/
- private boolean isInteger;
+ private boolean dataTypeisInteger;
/**
* Creates an initially empty grid view. The content can be set after
@@ -292,20 +301,20 @@ public class GridView extends Control {
final RenderedImage previous, final
RenderedImage image)
{
tiles.clear(); // Let garbage collector dispose the rasters.
- lastTile = null;
- width = 0;
- height = 0;
- isInteger = false;
+ lastTile = null;
+ width = 0;
+ height = 0;
if (image != null) {
- width = image.getWidth();
- height = image.getHeight();
- minX = image.getMinX();
- minY = image.getMinY();
- tileWidth = image.getTileWidth();
- tileHeight = image.getTileHeight();
- tileGridXOffset = Math.subtractExact(image.getTileGridXOffset(),
minX);
- tileGridYOffset = Math.subtractExact(image.getTileGridYOffset(),
minY);
- numXTiles = image.getNumXTiles();
+ minX = image.getMinX();
+ minY = image.getMinY();
+ width = image.getWidth();
+ height = image.getHeight();
+ numXTiles = image.getNumXTiles();
+ tileWidth = Math.max(1, image.getTileWidth());
+ tileHeight = Math.max(1, image.getTileHeight());
+ tileGridXOffset = Math.subtractExact(image.getTileGridXOffset(),
minX);
+ tileGridYOffset = Math.subtractExact(image.getTileGridYOffset(),
minY);
+ dataTypeisInteger = false; // To be kept
consistent with `cellFormat` pattern.
final SampleModel sm = image.getSampleModel();
if (sm != null) { // Should never be
null, but we are paranoiac.
final int numBands = sm.getNumBands();
@@ -313,19 +322,19 @@ public class GridView extends Control {
bandProperty.set(numBands - 1);
}
final int dataType = sm.getDataType();
- isInteger = (dataType >= DataBuffer.TYPE_BYTE && dataType <=
DataBuffer.TYPE_INT);
- if (isInteger) {
- cellFormat.setMaximumFractionDigits(0);
- } else {
- /*
- * TODO: compute the number of fraction digits from a
"sampleResolution" image property
- * (of type float[] or double[]) if present. Provide a
widget allowing user to set pattern.
- */
- cellFormat.setMinimumFractionDigits(1);
- cellFormat.setMaximumFractionDigits(1);
- }
- formatChanged(false);
+ dataTypeisInteger = (dataType >= DataBuffer.TYPE_BYTE &&
dataType <= DataBuffer.TYPE_INT);
+ }
+ if (dataTypeisInteger) {
+ cellFormat.setMaximumFractionDigits(0);
+ } else {
+ /*
+ * TODO: compute the number of fraction digits from a
"sampleResolution" image property
+ * (of type float[] or double[]) if present. Provide a widget
allowing user to set pattern.
+ */
+ cellFormat.setMinimumFractionDigits(1);
+ cellFormat.setMaximumFractionDigits(1);
}
+ formatChanged(false);
contentChanged(true);
}
}
@@ -382,6 +391,24 @@ public class GridView extends Control {
}
/**
+ * Returns the bounds of a single tile in the image. This method is
invoked only
+ * if an error occurred during {@link RenderedImage#getTile(int, int)}
invocation.
+ * The returned bounds are zero-based (may not be the bounds in image
coordinates).
+ *
+ * <div class="note"><b>Note:</b> we use AWT rectangle instead than JavaFX
rectangle
+ * because generally we use AWT for everything related to {@link
RenderedImage}.</div>
+ *
+ * @param tileX <var>x</var> coordinates of the tile for which to get
the bounds.
+ * @param tileY <var>y</var> coordinates of the tile for which to get
the bounds.
+ * @return the zero-based bounds of the specified tile in the image.
+ */
+ final Rectangle getTileBounds(final int tileX, final int tileY) {
+ return new Rectangle(tileX * tileWidth + tileGridXOffset,
+ tileY * tileHeight + tileGridYOffset,
+ tileWidth, tileHeight);
+ }
+
+ /**
* Converts a grid row index to image <var>y</var> coordinate. Those
values may differ
* because the image coordinate system does not necessarily starts at zero.
*
@@ -421,41 +448,52 @@ public class GridView extends Control {
* @see GridRow#getSampleValue(int)
*/
final String getSampleValue(final int y, final int tileY, final int
column) {
- if (y >= 0 && y < height && column >= 0 && column < width) {
- final int tileX = Math.floorDiv(Math.subtractExact(column,
tileGridXOffset), tileWidth);
- GridTile cache = lastTile;
- if (cache == null || cache.tileX != tileX || cache.tileY != tileY)
{
- final GridTile key = new GridTile(tileX, tileY, numXTiles);
- cache = tiles.putIfAbsent(key, key);
- if (cache == null) cache = key;
- lastTile = cache;
- }
- Raster tile = cache.tile;
- if (tile == null) {
- cache.load(this);
- return null;
+ if (y < 0 || y >= height || column < 0 || column >= width) {
+ return OUT_OF_BOUNDS;
+ }
+ /*
+ * Fetch the tile where is located the (x,y) image coordinate of the
pixel to get.
+ * If that tile has never been requested before, or has been discarded
by the cache,
+ * start a background thread for fetching the tile and return null
immediately; this
+ * method will be invoked again with the same coordinates after the
tile become ready.
+ */
+ final int tileX = Math.floorDiv(Math.subtractExact(column,
tileGridXOffset), tileWidth);
+ GridTile cache = lastTile;
+ if (cache == null || cache.tileX != tileX || cache.tileY != tileY) {
+ final GridTile key = new GridTile(tileX, tileY, numXTiles);
+ cache = tiles.putIfAbsent(key, key);
+ if (cache == null) cache = key;
+ lastTile = cache;
+ }
+ Raster tile = cache.tile();
+ if (tile == null) {
+ cache.load(this);
+ return null;
+ }
+ /*
+ * At this point we have the tile. Get the desired number and format
its string representation.
+ * As a slight optimization, we reuse the previous string
representation if the number is the same.
+ * It may happen in particular with fill values.
+ */
+ final int x = Math.addExact(column, minX);
+ final int b = getBand();
+ buffer.setLength(0);
+ if (dataTypeisInteger) {
+ final int integer = tile.getSample(x, y, b);
+ final double value = integer;
+ if (Double.doubleToRawLongBits(value) !=
Double.doubleToRawLongBits(lastValue)) {
+ // The `format` method invoked here is not the same than in
`double` case.
+ lastValueAsText = cellFormat.format(integer, buffer,
formatField).toString();
+ lastValue = value;
}
- final int x = Math.addExact(column, minX);
- final int b = getBand();
- buffer.setLength(0);
- if (isInteger) {
- final int integer = tile.getSample(x, y, b);
- final double value = integer;
- if (Double.doubleToRawLongBits(value) !=
Double.doubleToRawLongBits(lastValue)) {
- // The `format` method invoked here is not the same than
in `double` case.
- lastValueAsText = cellFormat.format(integer, buffer,
formatField).toString();
- lastValue = value;
- }
- } else {
- final double value = tile.getSampleDouble(x, y, b);
- if (Double.doubleToRawLongBits(value) !=
Double.doubleToRawLongBits(lastValue)) {
- lastValueAsText = cellFormat.format(value, buffer,
formatField).toString();
- lastValue = value;
- }
+ } else {
+ final double value = tile.getSampleDouble(x, y, b);
+ if (Double.doubleToRawLongBits(value) !=
Double.doubleToRawLongBits(lastValue)) {
+ lastValueAsText = cellFormat.format(value, buffer,
formatField).toString();
+ lastValue = value;
}
- return lastValueAsText;
}
- return OUT_OF_BOUNDS;
+ return lastValueAsText;
}
/**
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 267150f..b3fb8f9 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
@@ -110,6 +110,12 @@ final class GridViewSkin extends
VirtualContainerBase<GridView, GridRow> {
double cellInnerWidth;
/**
+ * The controls to show in case of error, or {@code null} if none. This is
created and
+ * added to the children list the first time that an error occurs while
reading a tile.
+ */
+ private GridError error;
+
+ /**
* Creates a new skin for the specified view.
*/
GridViewSkin(final GridView view) {
@@ -128,7 +134,7 @@ final class GridViewSkin extends
VirtualContainerBase<GridView, GridRow> {
view.headerWidth.addListener(this::cellWidthChanged);
/*
* Rectangles for filling the background of the cells in the header
row and header column.
- * Those rectangles will be resized and relocated in `layout(…)`
method.
+ * Those rectangles will be resized and relocated by the `layout(…)`
method.
*/
topBackground = new Rectangle();
leftBackground = new Rectangle();
@@ -165,18 +171,44 @@ final class GridViewSkin extends
VirtualContainerBase<GridView, GridRow> {
}
/**
+ * Invoked when an error occurred when fetching a tile. If this is the
first time that an error happens
+ * for this image, then a {@link GridError} node will be added as the last
child (i.e. will be drawn on
+ * top of everything else). That child will be removed if a new image is
set.
+ */
+ final void errorOccurred(final GridTile.Error status) {
+ if (error == null) {
+ error = new GridError();
+ getChildren().add(error);
+ }
+ 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);
+ }
+ }
+
+ /**
* Invoked when the content may have changed. If {@code all} is {@code
true}, then everything
* may have changed including the number of rows and columns. If {@code
all} is {@code false}
* then the number of rows and columns is assumed the same.
*
- * <p>This method is invoked by {@link GridView} when the image has
changed,
- * or the band in the image to show has changed.</p>
+ * <p>This method is invoked by {@link GridView} when the image has
changed ({@code all=true}),
+ * or the band in the image to show has changed ({@code all=false}).</p>
*
* @see GridView#contentChanged(boolean)
*/
final void contentChanged(final boolean all) {
if (all) {
updateItemCount();
+ if (error != null) {
+ error = null;
+ getChildren().removeIf((node) -> (node instanceof GridError));
+ }
}
/*
* Following call may be redundant with `updateItemCount()` except if
the number of
@@ -367,5 +399,21 @@ final class GridViewSkin extends
VirtualContainerBase<GridView, GridRow> {
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);
+ }
}
}