package me.coley.recaf.ui; import com.sun.javafx.application.PlatformImpl; import javafx.application.Application; import javafx.application.Platform; import javafx.geometry.Orientation; import javafx.scene.Scene; import javafx.scene.control.SplitPane; import javafx.scene.image.Image; import javafx.scene.layout.BorderPane; import javafx.stage.Stage; import me.coley.recaf.Recaf; import me.coley.recaf.control.gui.GuiController; import me.coley.recaf.ui.controls.*; import me.coley.recaf.ui.controls.popup.UpdateWindow; import me.coley.recaf.ui.controls.view.ClassViewport; import me.coley.recaf.ui.controls.view.FileViewport; import me.coley.recaf.util.ThreadUtil; import me.coley.recaf.util.self.SelfUpdater; import me.coley.recaf.workspace.JavaResource; import java.lang.management.ManagementFactory; import java.lang.management.PlatformLoggingMXBean; import java.lang.reflect.Field; import static me.coley.recaf.util.ClasspathUtil.resource; /** * Primary window. * * @author Matt */ public class MainWindow extends Application { private static MainWindow window; private final GuiController controller; private Stage stage; private BorderPane root; private BorderPane navRoot; private BorderPane viewRoot; private MainMenu menubar; private ViewportTabs tabs; private WorkspaceNavigator navigator; private MainWindow(GuiController controller) { this.controller = controller; } @Override public void start(Stage stage) throws Exception { // Set instances window = this; this.stage = stage; stage.setOnCloseRequest(e -> controller.exit()); setup(); stage.show(); } private void setup() { stage.getIcons().add(new Image(resource("icons/logo.png"))); stage.setTitle("Recaf"); menubar = new MainMenu(controller); root = new BorderPane(); root.setTop(menubar); navRoot = new BorderPane(); viewRoot = new BorderPane(); tabs = new ViewportTabs(controller); SplitPane split = new SplitPane(); split.setOrientation(Orientation.HORIZONTAL); split.getItems().addAll(navRoot, viewRoot); split.setDividerPositions(0.333); SplitPane.setResizableWithParent(navRoot, Boolean.FALSE); root.setCenter(split); viewRoot.setCenter(tabs); // Navigation updateWorkspaceNavigator(); Recaf.getWorkspaceSetListeners().add(w -> updateWorkspaceNavigator()); // Create scene & display the window Scene scene = new Scene(root, 800, 600); controller.windows().reapplyStyle(scene); controller.config().keys().registerMainWindowKeys(controller, stage, scene); stage.setScene(scene); // Show update prompt if (SelfUpdater.hasUpdate()) UpdateWindow.create(this).show(root); } private void updateWorkspaceNavigator() { Platform.runLater(() -> navRoot.setCenter(navigator = new WorkspaceNavigator(controller))); } /** * @param resource * Resource containing the class. * @param name * Name of class to open. * * @return Viewport of the class. */ public ClassViewport openClass(JavaResource resource, String name) { return tabs.openClass(resource, name); } /** * @param name * Name of class. * * @return Viewport of the class. {@code null} if the class is not currently open. */ public ClassViewport getClassViewport(String name) { return tabs.getClassViewport(name); } /** * @param resource * Resource containing the resource. * @param name * Name of file to open. * * @return Viewport of the file. */ public FileViewport openFile(JavaResource resource, String name) { return tabs.openFile(resource, name); } /** * @param name * Name of file. * * @return Viewport of the file. {@code null} if the file is not currently open. */ public FileViewport getFileViewport(String name) { return tabs.getFileViewport(name); } /** * @return Open class/file tabs. */ public ViewportTabs getTabs() { return tabs; } /** * Save the current application to a file. */ public void saveApplication() { menubar.saveApplication(); } /** * @return Initial stage. */ public Stage getStage() { return stage; } /** * @return Menubar. */ public MainMenu getMenubar() { return menubar; } /** * @return Workspace navigator. Will be {@code null} if the current workspace is {@code null}. */ public WorkspaceNavigator getNavigator() { return navigator; } /** * @param controller * Window context. * * @return main window instance. */ public static MainWindow get(GuiController controller) { if(window == null) { MainWindow app = window = new MainWindow(controller); PlatformImpl.startup(() -> { Stage stage = new Stage(); try { Field field = Stage.class.getDeclaredField("primary"); field.setAccessible(true); field.setBoolean(stage, true); app.init(); app.start(stage); } catch (Exception ex) { throw new RuntimeException(ex); } // Disable CSS logger, it complains a lot about non-issues try { ManagementFactory.getPlatformMXBean(PlatformLoggingMXBean.class) .setLoggerLevel("javafx.css", "OFF"); } catch (IllegalArgumentException ignored) { // Expected: logger may not exist } }); } return window; } /** * Clear open tabs and remove the current workspace navigator. */ public void clear() { if (tabs != null) tabs.getTabs().clear(); if(navRoot != null && navRoot.getCenter() != null) { WorkspaceNavigator nav = ((WorkspaceNavigator) navRoot.getCenter()); nav.enablePlaceholder(); nav.clear("..."); } } /** * Clear tab viewports */ public void clearTabViewports() { if (tabs != null) ThreadUtil.checkJfxAndEnqueue(() -> tabs.clearViewports()); } /** * Set disability status of window components. * * @param status * Disability status. */ public void disable(boolean status) { menubar.setDisable(status); if(tabs != null) tabs.setDisable(status); if(navRoot != null) navRoot.setDisable(status); } /** * Update the navigation pane with an informational message.<br> * Used when loading workspaces for visual feedback. * * @param status * Message to update. */ public void status(String status) { if (navRoot != null && navRoot.isDisable() && navRoot.getCenter() != null) ((WorkspaceNavigator) navRoot.getCenter()).clear(status); } /** * @param title * Window title to set. */ public void setTitle(String title) { ThreadUtil.checkJfxAndEnqueue(() -> stage.setTitle(title)); } }