package com.twasyl.slideshowfx.controls.notification;
import com.twasyl.slideshowfx.beans.properties.TaskStatusGlyphNameBinding;
import com.twasyl.slideshowfx.beans.properties.TaskStatusGlyphStyleBinding;
import com.twasyl.slideshowfx.utils.DialogHelper;
import com.twasyl.slideshowfx.utils.beans.binding.LocalTimeBinding;
import com.twasyl.slideshowfx.utils.concurrent.SlideshowFXTask;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon;
import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView;
import javafx.animation.Animation;
import javafx.animation.Interpolator;
import javafx.animation.RotateTransition;
import javafx.beans.property.ReadOnlyObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.geometry.Pos;
import javafx.scene.control.Button;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextArea;
import javafx.scene.layout.Border;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.util.Duration;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Represents a notification to be present in the {@link NotificationCenter}. It displays the title of the {@link SlideshowFXTask}
* that is associated to the notification, as well as an icon representing the status of the task and a button for deleting
* the notification in the notification center.
*
* @author Thierry Wasylczenko
* @version 1.0
* @since SlideshowFX 1.0
*/
public class Notification extends MenuItem {
private static final Logger LOGGER = Logger.getLogger(Notification.class.getName());
private final ReadOnlyObjectProperty<SlideshowFXTask> task = new SimpleObjectProperty<>();
public Notification(final SlideshowFXTask task) {
((SimpleObjectProperty) this.task).set(task);
final Text taskTitle = this.getTaskTitle();
final Text statusChangeTime = this.getStatusChangeTimeText();
final TextFlow statusFlow = new TextFlow(taskTitle, new Text("\n"), statusChangeTime);
statusFlow.setMaxWidth(250);
final FontAwesomeIconView statusIcon = this.getStatusIcon();
final Button deleteButton = this.getDeleteButton();
final HBox content = new HBox(5);
content.setAlignment(Pos.CENTER);
content.getChildren().addAll(statusIcon, statusFlow, deleteButton);
this.setGraphic(content);
this.setOnAction(event -> {
if (this.task.get().getState() == Worker.State.FAILED) {
final StringBuilder builder = new StringBuilder(this.task.get().getException().getMessage())
.append("\n");
try (final StringWriter stringWriter = new StringWriter();
final PrintWriter writer = new PrintWriter(stringWriter)) {
this.task.get().getException().printStackTrace(writer);
writer.flush();
builder.append(stringWriter.toString());
} catch (IOException ex) {
LOGGER.log(Level.SEVERE, "Can not parse error stacktrace", ex);
}
final TextArea errorMessage = new TextArea(builder.toString());
errorMessage.setPrefColumnCount(50);
errorMessage.setPrefRowCount(20);
errorMessage.setWrapText(true);
errorMessage.setEditable(false);
errorMessage.setBorder(Border.EMPTY);
DialogHelper.showError(this.task.get().getTitle(), errorMessage);
}
});
}
/**
* Initialize the {@link javafx.scene.Node} that displays the {@link Task#titleProperty() title} of the task.
* @return The {@link javafx.scene.Node} that displays the title of the task.
*/
private Text getTaskTitle() {
final Text taskTitle = new Text();
taskTitle.getStyleClass().addAll("text", "notification", "title");
taskTitle.textProperty().bind(this.task.get().titleProperty());
return taskTitle;
}
/**
* Initialize the {@link javafx.scene.Node} that displays the time when the status of the task has changed.
* @return The {@link javafx.scene.Node} that displays the change time of the status of the task.
*/
private Text getStatusChangeTimeText() {
final Text statusChangeTime = new Text();
statusChangeTime.getStyleClass().addAll("text", "notification", "time");
statusChangeTime.textProperty().bind(new LocalTimeBinding(this.task.get().statusChangedTimeProperty()));
return statusChangeTime;
}
/**
* Initialize the {@link javafx.scene.Node} that will contain the icon indicating the status (RUNNING, SUCCEEDED, FAILED, ...) of the
* notification.
* @return The {@link javafx.scene.Node} indicating the status of the notification.
*/
private FontAwesomeIconView getStatusIcon() {
final FontAwesomeIconView statusIcon = new FontAwesomeIconView();
final RotateTransition rotation = new RotateTransition(Duration.seconds(1), statusIcon);
rotation.setByAngle(360);
rotation.setCycleCount(Animation.INDEFINITE);
rotation.setInterpolator(Interpolator.LINEAR);
statusIcon.glyphNameProperty().addListener((glyphValue, oldGlyph, newGlyph) -> {
if(FontAwesomeIcon.SPINNER.name().equals(newGlyph)) rotation.playFromStart();
else {
rotation.stop();
statusIcon.setRotate(0);
}
});
statusIcon.glyphNameProperty().bind(new TaskStatusGlyphNameBinding(this.task.get()));
statusIcon.glyphStyleProperty().bind(new TaskStatusGlyphStyleBinding(this.task.get()));
statusIcon.setGlyphSize(20);
return statusIcon;
}
/**
* Initialize the {@link Button} that will allow to remove the notification from the {@link NotificationCenter}.
* @return The button for deleting the notification from the {@link NotificationCenter}.
*/
private Button getDeleteButton() {
final FontAwesomeIconView deleteIcon = new FontAwesomeIconView(FontAwesomeIcon.TIMES);
deleteIcon.setGlyphSize(10);
final Button deleteButton = new Button();
deleteButton.getStyleClass().add("notification");
deleteButton.setGraphic(deleteIcon);
deleteButton.setOnAction(event -> Notification.this.getParentPopup().getItems().remove(Notification.this));
return deleteButton;
}
/**
* Get the task associated to this notification.
* @return The property containing the task associated to this notification.
*/
public ReadOnlyObjectProperty<SlideshowFXTask> taskProperty() { return task; }
/**
* Get the task associated to this notification.
* @return The task associated to this notification.
*/
public Task getTask() { return task.get(); }
}