/*
* Copyright 2014 michael-simons.eu.
*
* Licensed 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 ac.simons.bikingFX.gallery;
import ac.simons.bikingFX.api.JsonRetrievalTask;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
/**
* @author Michael J. Simons, 2014-10-19
*/
public class GalleryPictureTableCell extends TableCell<GalleryPicture, Integer> {
/** Those table cells are created twice, so we keep track of images used here... Very trivial solution using global state. */
private static final Map<Integer, Image> images = new ConcurrentHashMap<>();
private final VBox container;
/** The current image that is or should be displayed */
private Image image;
/**
* Watches the progress of the given image to load. If the progress its 1.0
* it checks wether the image that is set for the given cell is still the
* same that was loaded.
*/
private class ImageLoadedListener implements ChangeListener<Number> {
private final Image imageToLoad;
public ImageLoadedListener(Image imageToLoad) {
this.imageToLoad = imageToLoad;
}
@Override
public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
if(newValue.intValue() == 1 && imageToLoad == GalleryPictureTableCell.this.image) {
displayImage(imageToLoad);
}
}
}
public GalleryPictureTableCell(TableColumn<GalleryPicture, Integer> column) {
this.container = new VBox(new ProgressIndicator());
this.container.setFillWidth(true);
this.container.setAlignment(Pos.CENTER);
this.container.setMinWidth(400);
this.container.setMaxWidth(800);
this.container.setMinHeight(300);
this.container.setMaxHeight(600);
setPadding(Insets.EMPTY);
setMaxWidth(800);
setMaxHeight(600);
setAlignment(Pos.CENTER);
setGraphic(container);
}
@Override
protected void updateItem(final Integer item, boolean empty) {
super.updateItem(item, empty);
if (item == null || empty) {
setText(null);
setGraphic(null);
} else {
this.image = images.computeIfAbsent(item, id -> new Image(String.format("%s/galleryPictures/%d.jpg", JsonRetrievalTask.BASE_URL, id), 800, 600, true, true, true));
if(image.getProgress() == 1.0) {
displayImage(image);
} else {
final ProgressIndicator progressIndicator = new ProgressIndicator();
container.getChildren().set(0, progressIndicator);
progressIndicator.progressProperty().bind(image.progressProperty());
image.progressProperty().addListener(new ImageLoadedListener(this.image));
}
setGraphic(container);
}
}
void displayImage(final Image image) {
final Node currentContent = this.container.getChildren().get(0);
if(!(currentContent instanceof ProgressIndicator)) {
final ImageView imageView = (ImageView) currentContent;
imageView.setImage(image);
} else {
final ImageView imageView = new ImageView(image);
imageView.fitWidthProperty().bind(this.container.widthProperty());
imageView.setPreserveRatio(true);
imageView.setCache(true);
this.container.getChildren().set(0, imageView);
}
}
}