package ch.retorte.intervalmusiccompositor.ui.audiofilelist;
import ch.retorte.intervalmusiccompositor.ChangeListener;
import ch.retorte.intervalmusiccompositor.audiofile.IAudioFile;
import ch.retorte.intervalmusiccompositor.commons.MessageFormatBundle;
import ch.retorte.intervalmusiccompositor.messagebus.DebugMessage;
import ch.retorte.intervalmusiccompositor.spi.MusicListControl;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer;
import com.google.common.primitives.Ints;
import javafx.collections.ObservableList;
import javafx.event.EventTarget;
import javafx.scene.control.ListView;
import javafx.scene.control.SelectionMode;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.TransferMode;
import java.io.File;
import java.util.Collection;
import java.util.List;
import static com.google.common.collect.Lists.newArrayList;
/**
* Draggable audio file trackList view supports drag and drop and shows the {@link IAudioFile}.
*/
public class DraggableAudioFileListView extends ListView<IAudioFile> {
private MessageProducer messageProducer;
private MusicListControl musicListControl;
private MessageFormatBundle messageFormatBundle;
private Collection<ChangeListener<ObservableList<IAudioFile>>> listChangeListeners = newArrayList();
public void initializeWith(ObservableList<IAudioFile> items, MessageFormatBundle messageFormatBundle, MessageProducer messageProducer, MusicListControl musicListControl) {
setItems(items);
this.messageFormatBundle = messageFormatBundle;
this.messageProducer = messageProducer;
this.musicListControl = musicListControl;
initializeSelection();
initializeDragNDrop();
initializeCellFactory();
initializeKeyActions();
}
public void addListChangeListener(ChangeListener<ObservableList<IAudioFile>> changeListener) {
listChangeListeners.add(changeListener);
}
void notifyListChangeListeners() {
listChangeListeners.forEach(cl -> cl.changed(getItems()));
}
private void initializeSelection() {
getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
}
private void initializeDragNDrop() {
setOnDragOver(event -> {
messageProducer.send(new DebugMessage(this, "Drag over detected."));
if (isValid(event.getDragboard())) {
messageProducer.send(new DebugMessage(this, "Dragboard is valid. Content types: " + event.getDragboard().getContentTypes()));
event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
}
else {
messageProducer.send(new DebugMessage(this, "Dragboard is not valid. Content types: " + event.getDragboard().getContentTypes()));
}
event.consume();
});
setOnDragDropped(event -> {
Dragboard db = event.getDragboard();
boolean success = false;
if (isValid(db)) {
List<File> files = db.getFiles();
if (files != null) {
files.forEach(this::addTrack);
success = true;
}
}
event.setDropCompleted(success);
event.consume();
});
}
private boolean isValid(Dragboard dragboard) {
return dragboard.hasFiles();
}
protected void initializeCellFactory() {
setCellFactory(items -> new AudioFileListCell(getMessageFormatBundle(), getMusicListControl(), getMessageProducer()));
}
protected MessageProducer getMessageProducer() {
return messageProducer;
}
MessageFormatBundle getMessageFormatBundle() {
return messageFormatBundle;
}
private void initializeKeyActions() {
addEventHandler(KeyEvent.KEY_RELEASED, event -> {
if (isDeleteCode(event.getCode())) {
EventTarget target = event.getTarget();
if (target instanceof DraggableAudioFileListView) {
DraggableAudioFileListView listView = (DraggableAudioFileListView) target;
removeTracks(toList(listView.getSelectionModel().getSelectedIndices()));
}
}
});
}
private boolean isDeleteCode(KeyCode keyCode) {
return keyCode == KeyCode.DELETE || keyCode == KeyCode.BACK_SPACE;
}
private <M> List<M> toList(ObservableList<M> list) {
List<M> result = newArrayList();
list.forEach(result::add);
return result;
}
public IAudioFile addTrack(File file) {
IAudioFile newTrack = musicListControl.appendMusicTrack(file);
addChangeListenerTo(newTrack);
notifyListChangeListeners();
return newTrack;
}
public void removeTrack(IAudioFile audioFile) {
getMusicListControl().removeMusicTrack(indexOf(audioFile));
notifyListChangeListeners();
}
int indexOf(IAudioFile audioFile) {
return trackList().indexOf(audioFile);
}
protected ObservableList<IAudioFile> trackList() {
return getMusicListControl().getMusicList();
}
public void removeTracks(List<Integer> indices) {
getMusicListControl().removeMusicTracks(Ints.toArray(indices));
notifyListChangeListeners();
}
void addChangeListenerTo(IAudioFile newTrack) {
if (newTrack != null) {
newTrack.addChangeListener(newStatus -> refresh());
}
}
MusicListControl getMusicListControl() {
return musicListControl;
}
public void writeBpmFor(IAudioFile audioFile) {
getMusicListControl().writeMusicBpm(indexOf(audioFile));
}
}