/* * To change this license header, choose License Headers in Project Properties. * To change this template file, choose Tools | Templates * and open the template in the editor. */ package jace; import com.sun.glass.ui.Application; import jace.cheat.MetaCheat; import jace.core.Card; import jace.core.Computer; import jace.core.Keyboard; import jace.library.MediaCache; import jace.library.MediaConsumer; import jace.library.MediaConsumerParent; import jace.library.MediaEntry; import java.io.File; import java.io.IOException; import java.net.URI; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.geometry.Insets; import javafx.scene.control.Label; import javafx.scene.effect.DropShadow; import javafx.scene.image.ImageView; import javafx.scene.input.DragEvent; import javafx.scene.input.KeyEvent; import javafx.scene.input.MouseEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.AnchorPane; import javafx.scene.layout.Background; import javafx.scene.layout.BackgroundFill; import javafx.scene.layout.CornerRadii; import javafx.scene.layout.HBox; import javafx.scene.layout.StackPane; import javafx.scene.paint.Color; import javafx.stage.Stage; /** * * @author blurry */ public class JaceUIController { @FXML private URL location; @FXML private AnchorPane rootPane; @FXML private StackPane stackPane; @FXML private HBox notificationBox; @FXML private ImageView appleScreen; Computer computer; @FXML void initialize() { assert rootPane != null : "fx:id=\"rootPane\" was not injected: check your FXML file 'JaceUI.fxml'."; assert stackPane != null : "fx:id=\"stackPane\" was not injected: check your FXML file 'JaceUI.fxml'."; assert notificationBox != null : "fx:id=\"notificationBox\" was not injected: check your FXML file 'JaceUI.fxml'."; assert appleScreen != null : "fx:id=\"appleScreen\" was not injected: check your FXML file 'JaceUI.fxml'."; appleScreen.fitWidthProperty().bind(rootPane.widthProperty()); appleScreen.fitHeightProperty().bind(rootPane.heightProperty()); rootPane.setOnDragEntered(this::processDragEnteredEvent); rootPane.setOnDragExited(this::processDragExitedEvent); } public void connectComputer(Computer computer, Stage primaryStage) { this.computer = computer; appleScreen.setImage(computer.getVideo().getFrameBuffer()); EventHandler<KeyEvent> keyboardHandler = computer.getKeyboard().getListener(); primaryStage.setOnShowing(evt -> computer.getKeyboard().resetState()); rootPane.setFocusTraversable(true); rootPane.setOnKeyPressed(keyboardHandler); rootPane.setOnKeyReleased(keyboardHandler); rootPane.requestFocus(); } private void processDragEnteredEvent(DragEvent evt) { MediaEntry media = null; if (evt.getDragboard().hasFiles()) { media = MediaCache.getMediaFromFile(getDraggedFile(evt.getDragboard().getFiles())); } else if (evt.getDragboard().hasUrl()) { media = MediaCache.getMediaFromUrl(evt.getDragboard().getUrl()); } else if (evt.getDragboard().hasString()) { String path = evt.getDragboard().getString(); try { URI.create(path); media = MediaCache.getMediaFromUrl(path); } catch (IllegalArgumentException ex) { File f = new File(path); if (f.exists()) { media = MediaCache.getMediaFromFile(f); } } } if (media != null) { evt.acceptTransferModes(TransferMode.LINK, TransferMode.COPY); startDragEvent(media); } } private void processDragExitedEvent(DragEvent evt) { endDragEvent(); } private File getDraggedFile(List<File> files) { if (files == null || files.isEmpty()) { return null; } for (File f : files) { if (f.exists()) { return f; } } return null; } HBox drivePanel; private void startDragEvent(MediaEntry media) { List<MediaConsumer> consumers = getMediaConsumers(); drivePanel = new HBox(); consumers.stream() .filter((consumer) -> (consumer.isAccepted(media, media.files.get(0)))) .forEach((consumer) -> { Label icon = consumer.getIcon().orElse(null); if (icon == null) { return; } icon.setTextFill(Color.WHITE); icon.setPadding(new Insets(2.0)); drivePanel.getChildren().add(icon); icon.setOnDragOver(event -> { event.acceptTransferModes(TransferMode.ANY); event.consume(); }); icon.setOnDragDropped(event -> { System.out.println("Dropping media on " + icon.getText()); try { computer.pause(); consumer.insertMedia(media, media.files.get(0)); computer.resume(); event.setDropCompleted(true); event.consume(); } catch (IOException ex) { Logger.getLogger(JaceUIController.class.getName()).log(Level.SEVERE, null, ex); } endDragEvent(); }); }); stackPane.getChildren().add(drivePanel); drivePanel.setLayoutX(10); drivePanel.setLayoutY(10); } private void endDragEvent() { stackPane.getChildren().remove(drivePanel); drivePanel.getChildren().stream().forEach((n) -> { n.setOnDragDropped(null); }); } private List<MediaConsumer> getMediaConsumers() { List<MediaConsumer> consumers = new ArrayList<>(); for (Optional<Card> card : computer.memory.getAllCards()) { card.filter(c -> c instanceof MediaConsumerParent).ifPresent(parent -> { consumers.addAll(Arrays.asList(((MediaConsumerParent) parent).getConsumers())); }); } return consumers; } Map<Label, Long> iconTTL = new ConcurrentHashMap<>(); void addIndicator(Label icon) { addIndicator(icon, 250); } void addIndicator(Label icon, long TTL) { if (!iconTTL.containsKey(icon)) { Application.invokeLater(() -> { if (!notificationBox.getChildren().contains(icon)) { notificationBox.getChildren().add(icon); } }); } trackTTL(icon, TTL); } void removeIndicator(Label icon) { Application.invokeLater(() -> { notificationBox.getChildren().remove(icon); iconTTL.remove(icon); }); } ScheduledExecutorService notificationExecutor = Executors.newSingleThreadScheduledExecutor(); ScheduledFuture ttlCleanupTask = null; private void trackTTL(Label icon, long TTL) { iconTTL.put(icon, System.currentTimeMillis() + TTL); if (ttlCleanupTask == null || ttlCleanupTask.isCancelled()) { ttlCleanupTask = notificationExecutor.scheduleWithFixedDelay(this::processTTL, 1, 100, TimeUnit.MILLISECONDS); } } private void processTTL() { Long now = System.currentTimeMillis(); iconTTL.keySet().stream() .filter((icon) -> (iconTTL.get(icon) <= now)) .forEach((icon) -> { removeIndicator(icon); }); if (iconTTL.isEmpty()) { ttlCleanupTask.cancel(true); ttlCleanupTask = null; } } public void addMouseListener(EventHandler<MouseEvent> handler) { appleScreen.addEventHandler(MouseEvent.ANY, handler); } public void removeMouseListener(EventHandler<MouseEvent> handler) { appleScreen.removeEventHandler(MouseEvent.ANY, handler); } Label currentNotification = null; public void displayNotification(String message) { Label oldNotification = currentNotification; Label notification = new Label(message); currentNotification = notification; notification.setEffect(new DropShadow(2.0, Color.BLACK)); notification.setTextFill(Color.WHITE); notification.setBackground(new Background(new BackgroundFill(Color.rgb(0,0,80, 0.7), new CornerRadii(5.0), new Insets(-5.0)))); Application.invokeLater(() -> { stackPane.getChildren().remove(oldNotification); stackPane.getChildren().add(notification); }); notificationExecutor.schedule(()->{ Application.invokeLater(() -> { stackPane.getChildren().remove(notification); }); }, 4, TimeUnit.SECONDS); } }