package com.soundlooper.gui.customComponent.playerView;
import java.io.File;
import java.net.MalformedURLException;
import java.util.Date;
import java.util.Timer;
import java.util.TimerTask;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.event.EventHandler;
import javafx.scene.control.SkinBase;
import javafx.scene.control.TextArea;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.InnerShadow;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Line;
import javafx.scene.shape.Rectangle;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import com.soundlooper.audio.player.Player;
import com.soundlooper.audio.player.Player.PlayerState;
import com.soundlooper.exception.PlayerException;
import com.soundlooper.exception.PlayerNotInitializedException;
import com.soundlooper.model.SoundLooperPlayer;
import com.soundlooper.model.mark.Mark;
import com.soundlooper.system.ImageGetter;
import com.soundlooper.system.SoundLooperColor;
import com.soundlooper.system.SoundLooperLigthing;
import com.soundlooper.system.util.MessagingUtil;
public class PlayerViewSkin extends SkinBase<PlayerView> {
private final static int LEFT_MARGIN = 30;
private final static int HANDLE_WIDTH = LEFT_MARGIN;
private final static int RIGTH_MARGIN = 30;
private final static int TOP_MARGIN = 30;
private final static int MIN_HANDLE_SPACING = 2;
private final static int DEFAULT_DURATION = 1000;
private Logger logger = LogManager.getLogger(this.getClass());
ChangeListener<Number> markTimeListener = new ChangeListener<Number>() {
@Override
public void changed(ObservableValue<? extends Number> arg0, Number arg1, Number arg2) {
getSkinnable().forceLayout();
}
};
private Rectangle loopBarBackground;
private Rectangle loopBarForeground;
private Rectangle unselectedZoneBegin;
private Rectangle unselectedZoneEnd;
private Line currentTimeLine;
private Line loopPointBeginLine;
private Line loopPointEndLine;
TextArea label = new TextArea("texte");
ImageView imageView;
ImageView leftHandleImage;
ImageView rightHandleImage;
private double dragStartLeftHandlePx;
private boolean leftHandleDrag = false;
private double dragStartRightHandlePx;
private boolean rightHandleDrag = false;
private double dragStartLoopBarForegroundPx;
private boolean loopBarForegroundDrag = false;
private double dragOffset;
private boolean invalidWidth = true;
private boolean invalidHeight = true;
Pane pane = new Pane();
protected PlayerViewSkin(PlayerView control) {
super(control);
control.setOnMousePressed(new EventHandler<MouseEvent>() {
@Override
public void handle(MouseEvent event) {
control.requestFocus();
}
});
control.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> arg0, Boolean oldPropertyValue,
Boolean newPropertyValue) {
if (!newPropertyValue) {
// must refresh on focus lost, to set control to "unfocused"
// aspect
control.requestLayout();
}
}
});
SoundLooperPlayer soundLooperPlayer = this.getSkinnable().getSoundLooperPlayer();
soundLooperPlayer.markProperty().addListener(new ChangeListener<Mark>() {
@Override
public void changed(ObservableValue<? extends Mark> observable, Mark oldValue, Mark newValue) {
PlayerViewSkin.this.getSkinnable().forceLayout();
updateMarkTimeListener(oldValue, newValue);
};
});
updateMarkTimeListener(null, soundLooperPlayer.getCurrentMark());
soundLooperPlayer.currentSongImageProperty().addListener(new InvalidationListener() {
@Override
public void invalidated(Observable observable) {
logger.info("The image is changing in the player view");
File file = soundLooperPlayer.currentSongImageProperty().get();
try {
imageView.setImage(new Image(file.toURI().toURL().toExternalForm()));
imageView.setPreserveRatio(false);
PlayerViewSkin.this.getSkinnable().forceLayout();
} catch (MalformedURLException e) {
// This exception will normally never be throwed
logger.error("Unable to get new Image", e);
}
}
});
control.focusedProperty().addListener(new ChangeListener<Boolean>() {
@Override
public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
if (newValue) {
control.forceLayout();
}
}
});
imageView = new ImageView(ImageGetter.getIconURL("loading_32.png"));
imageView.setPreserveRatio(false);
imageView.setEffect(new InnerShadow());
rightHandleImage = ImageGetter.getIcon("rightHandle.png");
rightHandleImage.setEffect(new DropShadow());
leftHandleImage = ImageGetter.getIcon("leftHandle.png");
leftHandleImage.setEffect(new DropShadow());
loopPointBeginLine = new Line(0, TOP_MARGIN, 0, 0);
loopPointEndLine = new Line(0, TOP_MARGIN, 0, 0);
loopBarBackground = new Rectangle(LEFT_MARGIN, 0, 0, TOP_MARGIN);
InnerShadow shadowLoopbarBackground = new InnerShadow();
shadowLoopbarBackground.setInput(SoundLooperLigthing.getBarLighting());
loopBarBackground.setEffect(shadowLoopbarBackground);
loopBarBackground.setFill(SoundLooperColor.WHITE);
loopBarForeground = new Rectangle(LEFT_MARGIN, 0, 0, TOP_MARGIN);
DropShadow shadowLoopbarForeground = new DropShadow();
shadowLoopbarForeground.setInput(SoundLooperLigthing.getBarLighting());
loopBarForeground.setEffect(shadowLoopbarForeground);
loopBarForeground.setFill(SoundLooperColor.DARK_GRAY);
unselectedZoneBegin = new Rectangle(LEFT_MARGIN, TOP_MARGIN, 0, 0);
unselectedZoneBegin.setFill(new Color(0, 0, 0, 0.20));
unselectedZoneEnd = new Rectangle(0, TOP_MARGIN, 0, 0);
unselectedZoneEnd.setFill(new Color(0, 0, 0, 0.20));
currentTimeLine = new Line(0, 0, 100, 100);
currentTimeLine.setStroke(Color.RED);
loopPointEndLine.startXProperty().bind(rightHandleImage.xProperty());
loopPointEndLine.endXProperty().bind(rightHandleImage.xProperty());
unselectedZoneEnd.xProperty().bind(rightHandleImage.xProperty());
DoubleBinding leftPropertyBinding = leftHandleImage.xProperty().add(HANDLE_WIDTH);
loopPointBeginLine.startXProperty().bind(leftPropertyBinding);
loopPointBeginLine.endXProperty().bind(leftPropertyBinding);
unselectedZoneBegin.widthProperty().bind(leftPropertyBinding.subtract(LEFT_MARGIN));
loopBarForeground.xProperty().bind(leftPropertyBinding);
loopBarForeground.widthProperty().bind(rightHandleImage.xProperty().subtract(leftPropertyBinding));
getChildren().add(pane);
pane.getChildren().add(imageView);
pane.getChildren().add(currentTimeLine);
pane.getChildren().add(loopBarBackground);
pane.getChildren().add(loopBarForeground);
pane.getChildren().add(loopPointBeginLine);
pane.getChildren().add(loopPointEndLine);
pane.getChildren().add(rightHandleImage);
pane.getChildren().add(leftHandleImage);
pane.getChildren().add(unselectedZoneBegin);
pane.getChildren().add(unselectedZoneEnd);
// getChildren().add(label);
control.widthProperty().addListener(l -> {
invalidWidth = true;
});
control.heightProperty().addListener(l -> {
invalidHeight = true;
});
startTimer();
}
protected void updateMarkTimeListener(Mark oldValue, Mark newValue) {
if (oldValue != null) {
oldValue.beginMillisecondProperty().removeListener(markTimeListener);
oldValue.endMillisecondProperty().removeListener(markTimeListener);
}
if (newValue != null) {
newValue.beginMillisecondProperty().addListener(markTimeListener);
newValue.endMillisecondProperty().addListener(markTimeListener);
}
}
private double getScreenRight(double contentWidth) {
return contentWidth - RIGTH_MARGIN;
}
@Override
protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {
PlayerView playerView = getSkinnable();
SoundLooperPlayer player = playerView.getSoundLooperPlayer();
int mediaTime;
int loopPointBegin;
int loopPointEnd;
int duration = 0;
if (!player.isSoundInitialized()) {
// there is no sound loaded, display en empty player
mediaTime = 0;
loopPointBegin = 0;
loopPointEnd = DEFAULT_DURATION;
duration = DEFAULT_DURATION;
} else {
mediaTime = player.getMediaTime();
loopPointBegin = player.getLoopPointBeginMillisecond();
loopPointEnd = player.getLoopPointEndMillisecond();
try {
duration = player.getCurrentSound().getDuration();
} catch (PlayerException e) {
logger.warn("unable to get duration", e);
}
}
// boundRectangle.setHeight(contentHeight);
// boundRectangle.setWidth(contentWidth);
double loopPointBeginPx = convertMsToPx(contentWidth, loopPointBegin, duration, true);
double loopPointEndPx = convertMsToPx(contentWidth, loopPointEnd, duration, true);
// left handle and decorations
if (!leftHandleDrag && !loopBarForegroundDrag) {
leftHandleImage.setX(loopPointBeginPx - HANDLE_WIDTH);
}
if (!rightHandleDrag && !loopBarForegroundDrag) {
rightHandleImage.setX(loopPointEndPx);
}
loopPointBeginLine.setEndY(contentHeight);
loopPointEndLine.setEndY(contentHeight);
double screenRight = getScreenRight(contentWidth);
unselectedZoneEnd.setWidth(screenRight - rightHandleImage.getX());
if (invalidWidth || invalidHeight) {
loopBarBackground.setWidth(getScreenWidth(contentWidth));
double screenHeight = getScreenHeight(contentHeight);
unselectedZoneBegin.setHeight(screenHeight);
System.out.println("SCREEN HEIGHT : " + screenHeight + " - Content : " + contentHeight);
unselectedZoneEnd.setHeight(screenHeight);
pane.setPrefSize(contentWidth, contentHeight);
imageView.setFitHeight(screenHeight);
imageView.setFitWidth(getScreenWidth(contentWidth));
imageView.setX(LEFT_MARGIN);
imageView.setY(TOP_MARGIN);
imageView.setOnMouseClicked(me -> {
double newTimeMs = convertPxToMs(PlayerViewSkin.this.getSkinnable().getWidth(), me.getX());
setMediaTime(player, new Double(newTimeMs).intValue());
playerView.forceLayout();
me.consume();
});
}
double mediaTimePx = convertMsToPx(contentWidth, mediaTime, duration, true);
currentTimeLine.setStartX(mediaTimePx);
currentTimeLine.setEndX(mediaTimePx);
currentTimeLine.setStartY(TOP_MARGIN);
currentTimeLine.setEndY(contentHeight);
if (invalidWidth) {
invalidWidth = false;
if (dragStartLeftHandlePx == 0) {
leftHandleImage.setOnMousePressed(me -> {
leftHandleDrag = true;
dragOffset = me.getSceneX() - leftHandleImage.getX();
dragStartLeftHandlePx = me.getSceneX();
});
leftHandleImage.setOnMouseDragged(me -> {
double move = me.getSceneX() - dragStartLeftHandlePx;
double newHandleX = dragStartLeftHandlePx + move - dragOffset;
if (newHandleX < 0) {
newHandleX = 0;
}
if (newHandleX + HANDLE_WIDTH + MIN_HANDLE_SPACING > rightHandleImage.getX()) {
newHandleX = rightHandleImage.getX() - HANDLE_WIDTH - MIN_HANDLE_SPACING;
}
leftHandleImage.setX(newHandleX);
});
leftHandleImage.setOnMouseReleased(me -> {
double newTimePx = leftHandleImage.getX() + HANDLE_WIDTH;
double newTimeMs = convertPxToMs(contentWidth, newTimePx);
leftHandleDrag = false;
dragStartLeftHandlePx = 0;
setMediaTimeIfNeeded(player, contentWidth);
setLoopPointBegin(player, newTimeMs);
playerView.forceLayout();
me.consume();
});
}
if (dragStartRightHandlePx == 0) {
rightHandleImage.setOnMousePressed(me -> {
rightHandleDrag = true;
dragOffset = me.getSceneX() - rightHandleImage.getX();
dragStartRightHandlePx = me.getSceneX();
});
rightHandleImage.setOnMouseDragged(me -> {
double move = me.getSceneX() - dragStartRightHandlePx;
double newHandleX = dragStartRightHandlePx + move - dragOffset;
if (newHandleX > screenRight) {
newHandleX = screenRight;
}
if (newHandleX - MIN_HANDLE_SPACING < leftHandleImage.getX() + HANDLE_WIDTH) {
newHandleX = MIN_HANDLE_SPACING + leftHandleImage.getX() + HANDLE_WIDTH;
}
rightHandleImage.setX(newHandleX);
});
rightHandleImage.setOnMouseReleased(me -> {
double newTimePx = rightHandleImage.getX();
double newTimeMs = convertPxToMs(contentWidth, newTimePx);
dragStartRightHandlePx = 0;
rightHandleDrag = false;
setMediaTimeIfNeeded(player, contentWidth);
setLoopPointEnd(player, newTimeMs);
playerView.forceLayout();
me.consume();
});
}
if (dragStartLoopBarForegroundPx == 0) {
loopBarForeground.setOnMousePressed(me -> {
loopBarForegroundDrag = true;
dragOffset = me.getSceneX() - loopBarForeground.getX();
dragStartLoopBarForegroundPx = me.getSceneX();
});
loopBarForeground.setOnMouseDragged(me -> {
double move = me.getSceneX() - dragStartLoopBarForegroundPx;
double newHandleX = dragStartLoopBarForegroundPx + move - dragOffset;
double positionLeftHandle = newHandleX - HANDLE_WIDTH;
double positionRightHandle = newHandleX + loopBarForeground.getWidth();
if (positionLeftHandle <= 0) {
positionLeftHandle = 0;
positionRightHandle = loopBarForeground.getWidth() + HANDLE_WIDTH;
}
if (positionRightHandle >= screenRight) {
positionRightHandle = screenRight;
positionLeftHandle = screenRight - loopBarForeground.getWidth() - HANDLE_WIDTH;
}
leftHandleImage.setX(positionLeftHandle);
rightHandleImage.setX(positionRightHandle);
});
loopBarForeground.setOnMouseReleased(me -> {
double newTimeBeginPx = loopBarForeground.getX();
double newTimeEndPx = loopBarForeground.getX() + loopBarForeground.getWidth();
double newTimeBeginMs = convertPxToMs(contentWidth, newTimeBeginPx);
double newTimeEndMs = convertPxToMs(contentWidth, newTimeEndPx);
dragStartLoopBarForegroundPx = 0;
loopBarForegroundDrag = false;
// setMediaTimeIfNeeded(player, contentWidth);
setLoopPoints(player, newTimeBeginMs, newTimeEndMs);
playerView.forceLayout();
me.consume();
});
}
}
invalidWidth = false;
invalidHeight = false;
}
private void setLoopPointBegin(SoundLooperPlayer player, double newTimeBeginMs) {
try {
if (player.isSoundInitialized()) {
player.setLoopPointBegin(new Double(newTimeBeginMs).intValue());
}
} catch (PlayerException e) {
MessagingUtil.displayError("Impossible de modifier la position de d�but", e);
}
}
private void setLoopPoints(SoundLooperPlayer player, double newTimeBeginMs, double newTimeEndMs) {
try {
if (player.isSoundInitialized()) {
player.setLoopPoints(new Double(newTimeBeginMs).intValue(), new Double(newTimeEndMs).intValue());
}
} catch (PlayerException e) {
MessagingUtil.displayError("Impossible de modifier les positions", e);
}
}
private void setLoopPointEnd(SoundLooperPlayer player, double newTimeEndMs) {
try {
if (player.isSoundInitialized()) {
player.setLoopPointEnd(new Double(newTimeEndMs).intValue());
}
} catch (PlayerException e) {
MessagingUtil.displayError("Impossible de modifier la position de fin", e);
}
}
private void setMediaTimeIfNeeded(Player player, double contentWidth) {
if (currentTimeLine.getStartX() > loopPointEndLine.getStartX()
|| currentTimeLine.getStartX() < loopPointBeginLine.getStartX()) {
double newTime = convertPxToMs(contentWidth, loopPointBeginLine.getStartX());
setMediaTime(player, newTime);
}
}
private void setMediaTime(Player player, double newTime) {
try {
if (player.isSoundInitialized()) {
player.setMediaTime(new Double(newTime).intValue());
}
} catch (PlayerException e) {
MessagingUtil.displayError("Impossible de modifier la position actuelle", e);
}
}
private double convertMsToPx(double contentWidth, int ms, int totalMs, boolean addMargin) {
if (totalMs == 0) {
return 0;
}
int margin = 0;
if (addMargin) {
margin = LEFT_MARGIN;
}
return margin + (getScreenWidth(contentWidth) * ms) / totalMs;
}
private double convertPxToMs(double contentWidth, double px) {
double pxInScreen = px - LEFT_MARGIN;
int duration = getDuration();
double screenWidth = getScreenWidth(contentWidth);
double msPerPx = duration / screenWidth;
return msPerPx * pxInScreen;
}
private int getDuration() {
if (SoundLooperPlayer.getInstance().isSoundInitialized()) {
try {
return SoundLooperPlayer.getInstance().getCurrentSound().getDuration();
} catch (PlayerNotInitializedException e) {
MessagingUtil.displayError("Impossible de r�cup�rer la dur�e", e);
}
}
return DEFAULT_DURATION;
}
private double getScreenWidth(double contentWidth) {
return contentWidth - LEFT_MARGIN - RIGTH_MARGIN;
}
private double getScreenHeight(double contentHeight) {
return contentHeight - TOP_MARGIN;
}
public void startTimer() {
Timer timerSlide = new Timer();
timerSlide.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
SoundLooperPlayer soundLooperPlayer = SoundLooperPlayer.getInstance();
if (soundLooperPlayer.getState() == PlayerState.STATE_PLAYING) {
PlayerViewSkin.this.getSkinnable().forceLayout();
}
}
}, new Date(), 100);
}
}