package com.twasyl.slideshowfx.ui.controls; import com.twasyl.slideshowfx.utils.Jar; import de.jensd.fx.glyphs.fontawesome.FontAwesomeIcon; import de.jensd.fx.glyphs.fontawesome.FontAwesomeIconView; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.ToggleButton; import javafx.scene.control.Tooltip; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.layout.VBox; import javafx.scene.text.Text; import javafx.scene.text.TextAlignment; import java.io.*; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.logging.Level; import java.util.logging.Logger; /** * Implementation of a {@link ToggleButton} representing a file of a plugin. The button has a CSS class named * {@code plugin-file-button}. * * @author Thierry Wasylczenko * @version 1.1 * @since SlideshowFX 1.1 */ public class PluginFileButton extends ToggleButton { private static Logger LOGGER = Logger.getLogger(PluginFileButton.class.getName()); private static final double BUTTON_SIZE = 80; private Jar pluginFile; private final String label; private final String version; private final String description; public PluginFileButton(final File pluginFile) { try { this.pluginFile = new Jar(pluginFile); } catch (IOException e) { throw new IllegalArgumentException("Invalid JAR file", e); } final Node icon = this.buildIconNode(this.pluginFile.getManifestAttributes()); this.label = this.pluginFile.getManifestAttributeValue("Setup-Wizard-Label", this.pluginFile.getFile().getName()); this.version = this.pluginFile.getManifestAttributeValue("Bundle-Version", ""); this.description = this.pluginFile.getManifestAttributeValue("Bundle-Description", ""); this.setPrefSize(BUTTON_SIZE, BUTTON_SIZE); this.setMinSize(BUTTON_SIZE, BUTTON_SIZE); this.setMaxSize(BUTTON_SIZE, BUTTON_SIZE); this.getStyleClass().add("plugin-file-button"); final VBox graphics = new VBox(2); graphics.setAlignment(Pos.CENTER); if (icon != null) { graphics.getChildren().add(icon); } else { graphics.getChildren().add(getLabelNode()); } graphics.getChildren().add(getVersionNode()); this.setGraphic(graphics); this.selectedProperty().addListener((selectedValue, oldSelected, newSelected) -> { final StringBuilder tooltipText = new StringBuilder(label).append(":\n") .append(description).append(".\n"); if (newSelected) tooltipText.append("Will be installed"); else tooltipText.append("Will not be installed"); tooltipText.append('.'); Tooltip tooltip = this.getTooltip(); if (tooltip == null) { tooltip = new Tooltip(); this.setTooltip(tooltip); } tooltip.setText(tooltipText.toString()); }); try { this.pluginFile.close(); } catch (IOException e) { LOGGER.log(Level.SEVERE, "Can not close plugin file", e); } } protected Text getVersionNode() { final Text versionElement = new Text(this.version); versionElement.getStyleClass().add("text"); versionElement.setWrappingWidth(75); versionElement.setTextAlignment(TextAlignment.CENTER); return versionElement; } protected Text getLabelNode() { final Text labelElement = new Text(this.label); labelElement.getStyleClass().add("text"); labelElement.setWrappingWidth(75); labelElement.setTextAlignment(TextAlignment.CENTER); return labelElement; } /** * Get the file associated to this button. * * @return The file associated to this button. */ public File getFile() { return this.pluginFile.getFile(); } /** * Get the label of this plugin. * * @return The label of this plugin. */ public String getLabel() { return this.label; } /** * Get the icon of the plugin stored within the JAR file as an array of bytes. If no icon is present, an empty array * is returned. * * @return The icon of the plugin. */ protected final byte[] getIconFromJar() { final ByteArrayOutputStream iconOut = new ByteArrayOutputStream(); final JarEntry icon = this.pluginFile.getEntry("META-INF/icon.png"); if (icon != null) { try (final InputStream iconIn = this.pluginFile.getInputStream(icon)) { final byte[] buffer = new byte[512]; int numberOfBytesRead; while ((numberOfBytesRead = iconIn.read(buffer)) != -1) { iconOut.write(buffer, 0, numberOfBytesRead); } iconOut.flush(); iconOut.close(); } catch (IOException e) { LOGGER.log(Level.WARNING, "Can not the icon from JAR", e); } } return iconOut.toByteArray(); } /** * Create the {@code Node} that will contain the icon of the plugin. * * @param attributes The manifest attributes of the plugin JAR file. * @return The element containing the icon of the plugin. */ protected final Node buildIconNode(final Attributes attributes) { Node icon = null; final byte[] iconFromJar = this.getIconFromJar(); if (iconFromJar != null && iconFromJar.length > 0) { final ByteArrayInputStream input = new ByteArrayInputStream(iconFromJar); final Image image = new Image(input, 50, 50, true, true); icon = new ImageView(image); } else { final String fontIconName = this.pluginFile.getManifestAttributeValue("Setup-Wizard-Icon-Name", ""); if (!fontIconName.isEmpty()) { icon = new FontAwesomeIconView(FontAwesomeIcon.valueOf(fontIconName)); ((FontAwesomeIconView) icon).setGlyphSize(50); } } return icon; } }