package ch.retorte.intervalmusiccompositor.ui.audiofilelist; import ch.retorte.intervalmusiccompositor.audiofile.IAudioFile; import ch.retorte.intervalmusiccompositor.commons.FormatTime; import ch.retorte.intervalmusiccompositor.commons.MessageFormatBundle; import ch.retorte.intervalmusiccompositor.commons.Utf8Control; import ch.retorte.intervalmusiccompositor.spi.MusicListControl; import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer; import ch.retorte.intervalmusiccompositor.ui.bpm.BpmWindow; import javafx.beans.property.SimpleBooleanProperty; import javafx.fxml.FXML; import javafx.fxml.FXMLLoader; import javafx.scene.control.*; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.*; import javafx.scene.paint.Color; import javafx.scene.text.Text; import java.io.IOException; import java.util.ResourceBundle; import static ch.retorte.intervalmusiccompositor.ui.IntervalMusicCompositorUI.UI_RESOURCE_BUNDLE_NAME; /** * Custom trackList cell for {@link IAudioFile}. */ class AudioFileListCell extends ListCell<IAudioFile> { //---- Fields private ResourceBundle resourceBundle = ResourceBundle.getBundle(UI_RESOURCE_BUNDLE_NAME, new Utf8Control()); private AudioFileListCellController listCellController; private MessageFormatBundle messageFormatBundle; private MusicListControl musicListControl; private MessageProducer messageProducer; //---- Constructor AudioFileListCell(MessageFormatBundle messageFormatBundle, MusicListControl musicListControl, MessageProducer messageProducer) { this.messageFormatBundle = messageFormatBundle; this.musicListControl = musicListControl; this.messageProducer = messageProducer; listCellController = new AudioFileListCellController(); itemProperty().addListener((observable, oldAudioFile, newAudioFile) -> { if (newAudioFile != null) { updateWith(newAudioFile); } else { setGraphic(null); } }); emptyProperty().addListener((observable, wasEmpty, isEmpty) -> { if (isEmpty) { setGraphic(null); } else { setGraphic(listCellController); } }); setContentDisplay(ContentDisplay.GRAPHIC_ONLY); } //---- Methods private DraggableAudioFileListView listView() { return (DraggableAudioFileListView) getListView(); } private void updateWith(IAudioFile audioFile) { listCellController.updateWith(getOrdinalNumberFor(audioFile), audioFile); } protected BpmWindow createBpmWindowFrom(MessageFormatBundle messageFormatBundle, MusicListControl musicListControl, MessageProducer messageProducer, IAudioFile audioFile, int index) { return new BpmWindow(messageFormatBundle, musicListControl, messageProducer, audioFile, index); } private int getOrdinalNumberFor(IAudioFile audioFile) { return getListView().getItems().indexOf(audioFile); } //---- Inner class private class AudioFileListCellController extends HBox { //---- Static private static final String LIST_CELL_LAYOUT_FILE = "/layouts/AudioFileListCell.fxml"; private static final String ORANGE = "#D7AD04"; private static final String BLUE = "#2F60AB"; private static final String GREEN = "#5BA853"; //---- Fields private SimpleBooleanProperty isBpmSupported = new SimpleBooleanProperty(false); private Image loading = new Image(getClass().getResource("/images/waiting_icon.gif").toString()); private Image ok = new Image(getClass().getResource("/images/music_icon.png").toString()); private Image error = new Image(getClass().getResource("/images/error_icon.png").toString()); private Image warning = new Image(getClass().getResource("/images/tooshort_icon.png").toString()); @FXML ImageView imageView; @FXML Text track; @FXML Text duration; @FXML Text bpm; @FXML Text title; @FXML Text status; //---- Constructor AudioFileListCellController() { loadFXML(); } //---- Methods private void loadFXML() { FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(LIST_CELL_LAYOUT_FILE), resourceBundle); fxmlLoader.setRoot(this); fxmlLoader.setController(this); try { fxmlLoader.load(); } catch (IOException exception) { throw new RuntimeException(exception); } } void updateWith(int ordinalNumber, IAudioFile audioFile) { setTrackWith(ordinalNumber); setDurationWith(audioFile); setBpmWith(audioFile); setTitleWith(audioFile); setStatusWith(audioFile); setContextMenuWith(audioFile); } private void setTrackWith(int ordinalNumber) { track.setText(String.valueOf(ordinalNumber + 1)); } private void setDurationWith(IAudioFile audioFile) { if (0 < audioFile.getDuration()) { duration.setText(format(audioFile.getDuration())); } else { duration.setText(null); } } private void setBpmWith(IAudioFile audioFile) { if (!audioFile.hasBpm()) { bpm.setText(""); return; } this.bpm.setText(String.valueOf(audioFile.getBpm()) + " bpm"); Color bpmColor = Color.web(ORANGE); if (audioFile.isBpmReliable()) { bpmColor = Color.web(GREEN); } if (audioFile.isBpmStored()) { bpmColor = Color.web(BLUE); } this.bpm.setFill(bpmColor); } private void setTitleWith(IAudioFile audioFile) { title.setText(audioFile.getDisplayName()); } private void setStatusWith(IAudioFile audioFile) { switch (audioFile.getStatus()) { case IN_PROGRESS: imageView.setImage(loading); status.setText(resourceBundle.getString("ui.form.music_list.loading_text")); return; case ERROR: imageView.setImage(error); status.setText(audioFile.getErrorMessage()); return; case OK: imageView.setImage(ok); status.setText(audioFile.getSource().getParentFile().getAbsolutePath()); return; default: imageView.setImage(warning); } } private void setContextMenuWith(IAudioFile audioFile) { setContextMenu(createContextMenu()); registerBpmStateListenerWith(audioFile); setBpmStateWith(audioFile); } private void registerBpmStateListenerWith(IAudioFile audioFile) { audioFile.addChangeListener(updatedAudioFile -> isBpmSupported.setValue(updatedAudioFile.isBpmSupported())); } private void setBpmStateWith(IAudioFile audioFile) { isBpmSupported.set(audioFile.isBpmSupported()); } private String format(long milliSeconds) { return new FormatTime().getStrictFormattedTime(milliSeconds / 1000); } private ContextMenu createContextMenu() { ContextMenu contextMenu = new ContextMenu(); contextMenu.getItems().add(createChangeBpmItem()); contextMenu.getItems().add(createWriteBpmItem()); contextMenu.getItems().add(separator()); contextMenu.getItems().add(createDeleteItem()); return contextMenu; } private MenuItem createChangeBpmItem() { MenuItem changeBpmItem = new MenuItem(); changeBpmItem.setText(resourceBundle.getString("ui.list_context.change_bpm")); changeBpmItem.setOnAction(event -> { BpmWindow bpmWindow = createBpmWindowFrom(messageFormatBundle, musicListControl, messageProducer, getItem(), getIndex()); bpmWindow.show(); }); changeBpmItem.disableProperty().bind(isBpmSupported.not()); return changeBpmItem; } private MenuItem createWriteBpmItem() { MenuItem writeBpmItem = new MenuItem(); writeBpmItem.setText(resourceBundle.getString("ui.list_context.write_bpm")); writeBpmItem.setOnAction(event -> listView().writeBpmFor(getItem())); writeBpmItem.disableProperty().bind(isBpmSupported.not()); return writeBpmItem; } private MenuItem separator() { return new SeparatorMenuItem(); } private MenuItem createDeleteItem() { MenuItem deleteItem = new MenuItem(); deleteItem.setText(resourceBundle.getString("ui.list_context.remove_track_sg")); deleteItem.setOnAction(event -> listView().removeTrack(getItem())); return deleteItem; } } }