package org.peerbox.presenter.settings.synchronization; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javafx.beans.property.BooleanProperty; import javafx.beans.property.ObjectProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleObjectProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.scene.control.CheckBoxTreeItem; import javafx.scene.control.Label; import javafx.scene.control.Tooltip; import javafx.scene.control.TreeItem; import javafx.scene.image.ImageView; public class SyncTreeItem extends CheckBoxTreeItem<PathItem> implements PropertyChangeListener { private static final Logger logger = LoggerFactory.getLogger(SyncTreeItem.class); private ImageView defaultIcon; private ImageView successIcon; private ImageView inProgressIcon; private ImageView errorIcon; private String sharingTooltip = ""; private PropertyChangeSupport mPcs = new PropertyChangeSupport(this); private BooleanProperty isShared = new SimpleBooleanProperty(false); private ObjectProperty<ProgressState> progressState = new SimpleObjectProperty<ProgressState>(); public SyncTreeItem(PathItem pathItem){ super(pathItem); Label label = new Label(pathItem.getPath().getFileName().toString()); setGraphic(label); pathItem.setLabel(label); initializeProgressStateProperty(); initializeIsSharedProperty(); initializeIcons(); setProgressState(ProgressState.DEFAULT); } public final boolean getIsShared(){ return isShared.get(); } public BooleanProperty isSharedProperty() { return isShared; } public final void setIsShared(Boolean newValue) { isShared.set(newValue); } public final ProgressState getProgressState(){ return progressState.get(); } public final void setProgressState(ProgressState newValue){ ProgressState oldProgressState = getProgressState(); progressState.set(newValue); mPcs.firePropertyChange("progressState", oldProgressState, progressState); } public ObjectProperty<ProgressState> progressStateProperty() { return progressState; } public void bindTo(SyncTreeItem other){ addPropertyChangeListener("progressState", other); getValue().bindPermissionsTo(other.getValue().getPermissionsSetProperty()); } public void unbind(){ mPcs = null; setValue(null); } @Override public void propertyChange(PropertyChangeEvent event) { logger.trace("{}: Changed property {} to {}", getValue().getPath(), event.getPropertyName(), event.getNewValue()); if (event.getPropertyName().equals("progressState")) { @SuppressWarnings("unchecked") ProgressState eventState = ((ObjectProperty<ProgressState>)event.getNewValue()).get(); switch(getProgressState()){ case IN_PROGRESS: handleWhenInProgress(eventState); break; case SUCCESSFUL: handleWhenSuccessful(eventState); break; case FAILED: break; default: handleWhenDefault(eventState); } } } private void initializeIsSharedProperty() { isShared.addListener(new ChangeListener<Boolean>(){ @Override public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { if(getValue().isFolder()){ if(newValue == true){ setSharedFolderIcons(); sharingTooltip = SynchronizationUtils.getSharedFolderTooltip(); } else { setDefaultFolderIcons(); } } //set to default to trigger change handler ProgressState temp = getProgressState(); setProgressState(ProgressState.DEFAULT); setProgressState(temp); } }); } private void setDefaultFolderIcons() { defaultIcon = SynchronizationUtils.getFolderStandardIcon(); errorIcon = SynchronizationUtils.getFolderErrorIcon(); successIcon = SynchronizationUtils.getFolderSuccessIcon(); inProgressIcon = SynchronizationUtils.getFolderInProgressIcon(); } private void setSharedFolderIcons() { defaultIcon = SynchronizationUtils.getSharedFolderStandardIcon(); errorIcon = SynchronizationUtils.getSharedFolderErrorIcon(); successIcon = SynchronizationUtils.getSharedFolderSuccessIcon(); inProgressIcon = SynchronizationUtils.getSharedFolderInProgressIcon(); } private void initializeProgressStateProperty() { progressState.addListener(new ChangeListener<ProgressState>() { @Override public void changed( ObservableValue<? extends ProgressState> arg0, ProgressState oldValue, ProgressState newValue) { handleProgressStateChange(oldValue, newValue); } }); } private void initializeIcons() { if(getValue().isFile()){ setFileIcons(); } else { setDefaultFolderIcons(); } } private void setFileIcons() { defaultIcon = SynchronizationUtils.getFileStandardIcon(); errorIcon = SynchronizationUtils.getFileErrorIcon(); successIcon = SynchronizationUtils.getFileSuccessIcon(); inProgressIcon = SynchronizationUtils.getFileInProgressIcon(); } public void addPropertyChangeListener(String property, PropertyChangeListener listener) { mPcs.addPropertyChangeListener(property, listener); } private void handleProgressStateChange(ProgressState oldValue, ProgressState newValue){ logger.trace("{} : Changed progressState from {} to {} ", getValue().getPath(), oldValue, newValue); if(oldValue == newValue){ updateIconInUIThread(); return; } switch(newValue){ case IN_PROGRESS: updateIconInUIThread(inProgressIcon); updateTooltipInUIThread(SynchronizationUtils.getInProgressToolTip()); break; case SUCCESSFUL: updateIconInUIThread(successIcon); updateTooltipInUIThread(SynchronizationUtils.getSuccessTooltip()); break; case FAILED: updateIconInUIThread(errorIcon); updateTooltipInUIThread(SynchronizationUtils.getErrorTooltip()); break; default: updateIconInUIThread(defaultIcon); updateTooltipInUIThread(SynchronizationUtils.getSuccessTooltip()); } } private void handleWhenDefault(ProgressState eventState) { switch(eventState){ case FAILED: //ignore break; default: setProgressState(eventState); } } private void handleWhenSuccessful(ProgressState eventState) { switch(eventState){ case IN_PROGRESS: setProgressState(eventState); break; default: } } private void handleWhenInProgress(ProgressState eventState) { switch(eventState){ case SUCCESSFUL: for(TreeItem<PathItem> child : getChildren()){ if(child instanceof SyncTreeItem){ SyncTreeItem castedChild = (SyncTreeItem) child; if(castedChild.getProgressState() == ProgressState.IN_PROGRESS){ return; } } } setProgressState(eventState); break; default: //ignore } } private void updateIconInUIThread(ImageView icon){ javafx.application.Platform.runLater(new Runnable() { @Override public void run() { if(getGraphic() != null && getGraphic() instanceof Label){ Label oldLabel = (Label)getGraphic(); oldLabel.setGraphic(icon); setGraphic(oldLabel); } else { Label newLabel = new Label(getValue().getPath().getFileName().toString()); newLabel.setGraphic(icon); setGraphic(newLabel); } } }); } private void updateIconInUIThread(){ ImageView iconToUse = getIconByProgressState(); updateIconInUIThread(iconToUse); } private ImageView getIconByProgressState() { switch(getProgressState()){ case IN_PROGRESS: return inProgressIcon; case SUCCESSFUL: return successIcon; case FAILED: return errorIcon; default: return defaultIcon; } } private void updateTooltipInUIThread(String tooltip){ String completeTooltip = tooltip.concat(sharingTooltip); javafx.application.Platform.runLater(new Runnable() { @Override public void run() { if(getGraphic() != null && getGraphic() instanceof Label){ Label oldLabel = (Label)getGraphic(); oldLabel.setTooltip(new Tooltip(completeTooltip)); setGraphic(oldLabel); } else { Label newLabel = new Label(getValue().getPath().getFileName().toString()); newLabel.setTooltip(new Tooltip(completeTooltip)); setGraphic(newLabel); } } }); } public void updateIsSelectedInUIThread(boolean b) { javafx.application.Platform.runLater(new Runnable() { @Override public void run() { if(getValue().getPath().toFile().isDirectory()){ if(b && allChildrenSynchronized()){ setSelected(b); } else { setIndeterminate(b); } } else { setSelected(b); } } private boolean allChildrenSynchronized() { return getChildren().stream().filter( item -> !((SyncTreeItem)item).isSelected()).count() == 0; } }); } }