package com.kartoflane.superluminal2.ui; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.HashMap; import net.vhati.ftldat.FTLDat.FTLPack; import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.Logger; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DropTarget; import org.eclipse.swt.dnd.DropTargetAdapter; import org.eclipse.swt.dnd.DropTargetEvent; import org.eclipse.swt.dnd.FileTransfer; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.MouseEvent; import org.eclipse.swt.events.MouseMoveListener; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.graphics.Color; import org.eclipse.swt.graphics.Image; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.graphics.RGB; import org.eclipse.swt.graphics.Rectangle; import org.eclipse.swt.layout.FormLayout; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Canvas; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Event; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Listener; import org.eclipse.swt.widgets.Menu; import org.eclipse.swt.widgets.MenuItem; import org.eclipse.swt.widgets.Monitor; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Spinner; import org.eclipse.swt.widgets.Text; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.ToolItem; import com.kartoflane.ftl.floorgen.FloorImageFactory; import com.kartoflane.superluminal2.Superluminal; import com.kartoflane.superluminal2.components.EventHandler; import com.kartoflane.superluminal2.components.Hotkey; import com.kartoflane.superluminal2.components.NotDeletableException; import com.kartoflane.superluminal2.components.Tuple; import com.kartoflane.superluminal2.components.enums.Hotkeys; import com.kartoflane.superluminal2.components.enums.Images; import com.kartoflane.superluminal2.components.enums.Modifiers; import com.kartoflane.superluminal2.components.interfaces.Action; import com.kartoflane.superluminal2.core.Cache; import com.kartoflane.superluminal2.core.Database; import com.kartoflane.superluminal2.core.DatabaseEntry; import com.kartoflane.superluminal2.core.Grid; import com.kartoflane.superluminal2.core.LayeredPainter; import com.kartoflane.superluminal2.core.Manager; import com.kartoflane.superluminal2.core.MouseInputDispatcher; import com.kartoflane.superluminal2.events.SLListener; import com.kartoflane.superluminal2.events.SLModAltEvent; import com.kartoflane.superluminal2.events.SLModCommandEvent; import com.kartoflane.superluminal2.events.SLModControlEvent; import com.kartoflane.superluminal2.events.SLModShiftEvent; import com.kartoflane.superluminal2.mvc.controllers.AbstractController; import com.kartoflane.superluminal2.mvc.controllers.CursorController; import com.kartoflane.superluminal2.mvc.controllers.DoorController; import com.kartoflane.superluminal2.mvc.controllers.GibController; import com.kartoflane.superluminal2.mvc.controllers.MountController; import com.kartoflane.superluminal2.mvc.controllers.RoomController; import com.kartoflane.superluminal2.mvc.controllers.ShipController; import com.kartoflane.superluminal2.tools.CreationTool; import com.kartoflane.superluminal2.tools.DoorTool; import com.kartoflane.superluminal2.tools.ImagesTool; import com.kartoflane.superluminal2.tools.ManipulationTool; import com.kartoflane.superluminal2.tools.MountTool; import com.kartoflane.superluminal2.tools.PropertyTool; import com.kartoflane.superluminal2.tools.RoomTool; import com.kartoflane.superluminal2.tools.StationTool; import com.kartoflane.superluminal2.tools.Tool.Tools; import com.kartoflane.superluminal2.ui.GibPropContainer.PropControls; import com.kartoflane.superluminal2.ui.SaveOptionsDialog.SaveOptions; import com.kartoflane.superluminal2.ui.sidebar.data.DataComposite; import com.kartoflane.superluminal2.undo.UndoableDeleteEdit; import com.kartoflane.superluminal2.undo.UndoableOffsetsEdit; import com.kartoflane.superluminal2.undo.UndoablePropertyEdit; import com.kartoflane.superluminal2.utils.SHPUtils; import com.kartoflane.superluminal2.utils.SWTFontUtils; import com.kartoflane.superluminal2.utils.UIUtils; import com.kartoflane.superluminal2.utils.Utils; @SuppressWarnings("serial") public class EditorWindow { static final Logger log = LogManager.getLogger(EditorWindow.class); public static final int SIDEBAR_MIN_WIDTH = 290; public static final int CANVAS_MIN_SIZE = 400; private static final RGB canvasRGB = new RGB(164, 164, 164); private static EditorWindow instance; private final HashMap<Tools, ToolItem> toolItemMap = new HashMap<Tools, ToolItem>(); private int sidebarWidth = SIDEBAR_MIN_WIDTH; private Color canvasColor = null; private boolean shellResizing = false; private static String prevShpPath = null; private EventHandler eventHandler = new EventHandler(); // UI widgets' variables private Shell shell; private ScrolledComposite sideContainer; private Canvas canvas; private ToolItem tltmPointer; private ToolItem tltmCreation; private ToolItem tltmProperties; private ToolItem tltmImages; private ToolItem tltmManager; private MenuItem mntmUndo; private MenuItem mntmRedo; private MenuItem mntmDelete; private MenuItem mntmShowAnchor; private MenuItem mntmShowMounts; private MenuItem mntmShowRooms; private MenuItem mntmShowDoors; private MenuItem mntmShowStations; private MenuItem mntmShowHull; private MenuItem mntmShowFloor; private MenuItem mntmShowShield; private MenuItem mntmShowGibs; private MenuItem mntmNewShip; private MenuItem mntmLoadShip; private MenuItem mntmConvertShp; private MenuItem mntmSettings; private MenuItem mntmGrid; private MenuItem mntmSaveShip; private MenuItem mntmSaveShipAs; private MenuItem mntmCloseShip; private SashForm editorContainer; private MenuItem mntmModMan; private ToolItem tltmCloak; private MenuItem mntmResetLinks; private MenuItem mntmOptimalOffset; private MenuItem mntmReloadDb; private MenuItem mntmHangar; private MenuItem mntmZoom; private MenuItem mntmGenerateFloor; private ToolItem tltmAnimate; private DropTarget dropTarget; private Label lblCursorLoc; public EditorWindow(Display display) { instance = this; shell = new Shell(display, SWT.SHELL_TRIM | SWT.SMOOTH); shell.setText(String.format("%s v%s - FTL Ship Editor", Superluminal.APP_NAME, Superluminal.APP_VERSION)); GridLayout gl_shell = new GridLayout(1, false); gl_shell.marginHeight = 0; gl_shell.marginWidth = 0; shell.setLayout(gl_shell); Monitor m = display.getPrimaryMonitor(); Rectangle displaySize = m.getClientArea(); displaySize.width = (displaySize.width / 5) * 4; displaySize.height = (displaySize.height / 5) * 4; // Load icons Image icon16 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_16.png"); Image icon24 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_24.png"); Image icon32 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_32.png"); Image icon48 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_48.png"); Image icon64 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_64.png"); Image icon128 = Cache.checkOutImage(shell, "cpath:/assets/icons/Superluminal_2_128.png"); Image[] icons = new Image[] { icon16, icon24, icon32, icon48, icon64, icon128 }; shell.setImages(icons); // Instantiate quasi-singletons new MouseInputDispatcher(); CursorController.newInstance(); Manager.putTool(Tools.POINTER, new ManipulationTool(this)); Manager.putTool(Tools.CREATOR, new CreationTool(this)); Manager.putTool(Tools.IMAGES, new ImagesTool(this)); Manager.putTool(Tools.CONFIG, new PropertyTool(this)); Manager.putTool(Tools.ROOM, new RoomTool(this)); Manager.putTool(Tools.DOOR, new DoorTool(this)); Manager.putTool(Tools.WEAPON, new MountTool(this)); Manager.putTool(Tools.STATION, new StationTool(this)); // Menu bar Menu menu = new Menu(shell, SWT.BAR); shell.setMenuBar(menu); // File menu MenuItem mntmFile = new MenuItem(menu, SWT.CASCADE); mntmFile.setText("File"); Menu menuFile = new Menu(mntmFile); mntmFile.setMenu(menuFile); mntmNewShip = new MenuItem(menuFile, SWT.NONE); mntmLoadShip = new MenuItem(menuFile, SWT.NONE); new MenuItem(menuFile, SWT.SEPARATOR); mntmConvertShp = new MenuItem(menuFile, SWT.NONE); mntmConvertShp.setText("Open .shp"); new MenuItem(menuFile, SWT.SEPARATOR); mntmSaveShip = new MenuItem(menuFile, SWT.NONE); mntmSaveShipAs = new MenuItem(menuFile, SWT.NONE); mntmSaveShipAs.setText("Save Ship As..."); new MenuItem(menuFile, SWT.SEPARATOR); mntmModMan = new MenuItem(menuFile, SWT.NONE); mntmReloadDb = new MenuItem(menuFile, SWT.NONE); mntmReloadDb.setText("Reload Database"); new MenuItem(menuFile, SWT.SEPARATOR); mntmCloseShip = new MenuItem(menuFile, SWT.NONE); // Edit menu MenuItem mntmEdit = new MenuItem(menu, SWT.CASCADE); mntmEdit.setText("Edit"); Menu menuEdit = new Menu(mntmEdit); mntmEdit.setMenu(menuEdit); mntmUndo = new MenuItem(menuEdit, SWT.NONE); mntmRedo = new MenuItem(menuEdit, SWT.NONE); new MenuItem(menuEdit, SWT.SEPARATOR); mntmResetLinks = new MenuItem(menuEdit, SWT.NONE); mntmResetLinks.setText("Reset All Door Links"); mntmOptimalOffset = new MenuItem(menuEdit, SWT.NONE); mntmOptimalOffset.setText("Calculate Optimal Offset"); mntmGenerateFloor = new MenuItem(menuEdit, SWT.NONE); mntmGenerateFloor.setText("Generate Floor Image"); new MenuItem(menuEdit, SWT.SEPARATOR); mntmDelete = new MenuItem(menuEdit, SWT.NONE); new MenuItem(menuEdit, SWT.SEPARATOR); mntmSettings = new MenuItem(menuEdit, SWT.NONE); // View menu MenuItem mntmView = new MenuItem(menu, SWT.CASCADE); mntmView.setText("View"); Menu menuView = new Menu(mntmView); mntmView.setMenu(menuView); mntmZoom = new MenuItem(menuView, SWT.NONE); new MenuItem(menuView, SWT.SEPARATOR); mntmGrid = new MenuItem(menuView, SWT.CHECK); mntmGrid.setSelection(true); mntmHangar = new MenuItem(menuView, SWT.CHECK); new MenuItem(menuView, SWT.SEPARATOR); MenuItem mntmShipComponents = new MenuItem(menuView, SWT.CASCADE); mntmShipComponents.setText("Ship Components"); Menu menuViewShip = new Menu(mntmShipComponents); mntmShipComponents.setMenu(menuViewShip); mntmShowAnchor = new MenuItem(menuViewShip, SWT.CHECK); mntmShowAnchor.setSelection(true); mntmShowMounts = new MenuItem(menuViewShip, SWT.CHECK); mntmShowMounts.setSelection(true); mntmShowRooms = new MenuItem(menuViewShip, SWT.CHECK); mntmShowRooms.setSelection(true); mntmShowDoors = new MenuItem(menuViewShip, SWT.CHECK); mntmShowDoors.setSelection(true); mntmShowStations = new MenuItem(menuViewShip, SWT.CHECK); mntmShowStations.setSelection(true); MenuItem mntmShipImages = new MenuItem(menuView, SWT.CASCADE); mntmShipImages.setText("Ship Images"); Menu menuViewImages = new Menu(mntmShipImages); mntmShipImages.setMenu(menuViewImages); mntmShowHull = new MenuItem(menuViewImages, SWT.CHECK); mntmShowHull.setSelection(true); mntmShowFloor = new MenuItem(menuViewImages, SWT.CHECK); mntmShowFloor.setSelection(true); mntmShowShield = new MenuItem(menuViewImages, SWT.CHECK); mntmShowShield.setSelection(true); mntmShowGibs = new MenuItem(menuViewImages, SWT.CHECK); mntmShowGibs.setSelection(true); // Help menu MenuItem mntmHelp = new MenuItem(menu, SWT.CASCADE); mntmHelp.setText("Help"); Menu menuHelp = new Menu(mntmView); mntmHelp.setMenu(menuHelp); MenuItem mntmUpdate = new MenuItem(menuHelp, SWT.NONE); mntmUpdate.setText("Check for Updates"); MenuItem mntmAbout = new MenuItem(menuHelp, SWT.NONE); mntmAbout.setText("About"); // Main container - contains everything else Composite mainContainer = new Composite(shell, SWT.NONE); mainContainer.setLayout(new GridLayout(1, false)); mainContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); // Tools container - tool bar, tools final Composite toolContainer = new Composite(mainContainer, SWT.NONE); toolContainer.setLayoutData(new GridData(SWT.FILL, SWT.TOP, true, false, 1, 1)); GridLayout gl_toolContainer = new GridLayout(2, false); gl_toolContainer.marginHeight = 0; gl_toolContainer.marginWidth = 0; toolContainer.setLayout(gl_toolContainer); // Tool bar widget ToolBar toolBar = new ToolBar(toolContainer, SWT.FLAT | SWT.RIGHT); toolBar.setLayoutData(new GridData(SWT.FILL, SWT.TOP, false, false, 1, 1)); SelectionAdapter toolSelectionAdapter = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.selectTool((Tools) ((ToolItem) e.getSource()).getData()); } }; // Pointer tool tltmPointer = new ToolItem(toolBar, SWT.RADIO); tltmPointer.setImage(Cache.checkOutImage(this, "cpath:/assets/pointer.png")); tltmPointer.addSelectionListener(toolSelectionAdapter); tltmPointer.setData(Tools.POINTER); toolItemMap.put(Tools.POINTER, tltmPointer); // Room tool tltmCreation = new ToolItem(toolBar, SWT.RADIO); tltmCreation.setImage(Cache.checkOutImage(this, "cpath:/assets/wrench.png")); tltmCreation.addSelectionListener(toolSelectionAdapter); tltmCreation.setData(Tools.CREATOR); toolItemMap.put(Tools.CREATOR, tltmCreation); // Images button tltmImages = new ToolItem(toolBar, SWT.RADIO); tltmImages.setImage(Cache.checkOutImage(this, "cpath:/assets/images.png")); tltmImages.addSelectionListener(toolSelectionAdapter); tltmImages.setData(Tools.IMAGES); toolItemMap.put(Tools.IMAGES, tltmImages); // Properties button tltmProperties = new ToolItem(toolBar, SWT.RADIO); tltmProperties.setImage(Cache.checkOutImage(this, "cpath:/assets/system.png")); tltmProperties.addSelectionListener(toolSelectionAdapter); tltmProperties.setData(Tools.CONFIG); toolItemMap.put(Tools.CONFIG, tltmProperties); new ToolItem(toolBar, SWT.SEPARATOR); // Manager button tltmManager = new ToolItem(toolBar, SWT.PUSH); tltmManager.setImage(Cache.checkOutImage(this, "cpath:/assets/overview.png")); tltmManager.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { OverviewWindow window = OverviewWindow.getInstance(); if (window.isDisposed()) window.init(shell); window.open(); } }); tltmCloak = new ToolItem(toolBar, SWT.CHECK); tltmCloak.setImage(Cache.checkOutImage(this, "cpath:/assets/cloak.png")); tltmCloak.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipContainer container = Manager.getCurrentShip(); container.setCloakedAppearance(tltmCloak.getSelection()); if (!tltmCloak.getSelection() && Manager.getSelected() == container.getImageController(Images.CLOAK)) Manager.setSelected(null); } }); tltmAnimate = new ToolItem(toolBar, SWT.PUSH); updateGibAnimationButton(true); tltmAnimate.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipContainer container = Manager.getCurrentShip(); if (container.isGibAnimationInProgress()) { container.stopGibAnimation(); } else { container.triggerGibAnimation(); } } }); // Info container - mouse position Composite infoContainer = new Composite(toolContainer, SWT.NONE); infoContainer.setLayout(new GridLayout(1, false)); infoContainer.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, true, 1, 1)); lblCursorLoc = new Label(infoContainer, SWT.NONE); lblCursorLoc.setAlignment(SWT.RIGHT); lblCursorLoc.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false, 1, 1)); lblCursorLoc.setFont(SWTFontUtils.getMonospacedFont(display)); // Editor container - canvas, sidebar editorContainer = new SashForm(mainContainer, SWT.SMOOTH); editorContainer.setSashWidth(7); editorContainer.setLayout(new FormLayout()); editorContainer.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); canvasColor = Cache.checkOutColor(this, canvasRGB); canvas = new Canvas(shell, SWT.DOUBLE_BUFFERED); canvas.setBackground(canvasColor); canvas.addPaintListener(LayeredPainter.getInstance()); sideContainer = new ScrolledComposite(shell, SWT.BORDER | SWT.V_SCROLL); sideContainer.setLayoutData(new GridData(SWT.LEFT, SWT.TOP, false, false, 1, 1)); sideContainer.setAlwaysShowScrollBars(true); sideContainer.setExpandHorizontal(true); sideContainer.setExpandVertical(true); sideContainer.getVerticalBar().setIncrement(15); if (Manager.sidebarOnRightSide) { canvas.setParent(editorContainer); sideContainer.setParent(editorContainer); editorContainer.setWeights(new int[] { displaySize.width - SIDEBAR_MIN_WIDTH, SIDEBAR_MIN_WIDTH }); } else { sideContainer.setParent(editorContainer); canvas.setParent(editorContainer); editorContainer.setWeights(new int[] { SIDEBAR_MIN_WIDTH, displaySize.width - SIDEBAR_MIN_WIDTH }); } display.addFilter(SWT.KeyUp, new Listener() { @Override public void handleEvent(Event e) { if ((e.keyCode == SWT.SPACE || e.keyCode == SWT.ARROW_UP || e.keyCode == SWT.ARROW_RIGHT || e.keyCode == SWT.ARROW_DOWN || e.keyCode == SWT.ARROW_LEFT) && Manager.getSelected() != null) e.doit = false; } }); display.addFilter(SWT.MouseEnter, new Listener() { @Override public void handleEvent(Event e) { shellResizing = false; } }); display.addFilter(SWT.FocusIn, new Listener() { public void handleEvent(Event e) { Control focus = UIUtils.getDisplay().getFocusControl(); if (focus == shell || focus == canvas || focus == sideContainer) { Database db = Database.getInstance(); if (db != null && db.getCore() != null && !db.verify()) { log.trace("Database failed to pass verification. Reload is required."); mntmReloadDb.notifyListeners(SWT.Selection, null); } } } }); sideContainer.addListener(SWT.Resize, new Listener() { public void handleEvent(Event e) { Grid.getInstance().updateBounds(canvas.getSize().x, canvas.getSize().y); ShipContainer ship = Manager.getCurrentShip(); if (ship != null) { ship.updateBoundingArea(); ship.updateChildBoundingAreas(); ship.getShipController().updateView(); ship.getShipController().updateProps(); ship.getShipController().redraw(); } if (!shellResizing) sidebarWidth = Math.max(sideContainer.getSize().x, SIDEBAR_MIN_WIDTH); } }); shell.addListener(SWT.Resize, new Listener() { public void handleEvent(Event e) { shellResizing = true; int width = shell.getClientArea().width; int[] weights = editorContainer.getWeights(); if (width >= sidebarWidth + CANVAS_MIN_SIZE) { weights[Manager.sidebarOnRightSide ? 1 : 0] = 1000000 * sidebarWidth / width; weights[Manager.sidebarOnRightSide ? 0 : 1] = 1000000 - weights[Manager.sidebarOnRightSide ? 1 : 0]; } else { weights[Manager.sidebarOnRightSide ? 1 : 0] = 1000000 * sidebarWidth / (sidebarWidth + CANVAS_MIN_SIZE); weights[Manager.sidebarOnRightSide ? 0 : 1] = 1000000 * CANVAS_MIN_SIZE / (sidebarWidth + CANVAS_MIN_SIZE); } editorContainer.setWeights(weights); if (!shell.getMaximized()) { Manager.windowSize.x = shell.getSize().x; Manager.windowSize.y = shell.getSize().y; } shell.layout(); Grid.getInstance().updateBounds(canvas.getSize().x, canvas.getSize().y); ShipContainer ship = Manager.getCurrentShip(); if (ship != null) { ship.updateBoundingArea(); ship.updateChildBoundingAreas(); ship.getShipController().updateView(); ship.getShipController().updateProps(); ship.getShipController().redraw(); } } }); shell.addListener(SWT.Close, new Listener() { @Override public void handleEvent(Event e) { e.doit = Manager.closeShip(); } }); mntmNewShip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { NewShipDialog dialog = new NewShipDialog(shell); int response = dialog.open(); if (response != -1) Manager.createNewShip(response == 0); } }); mntmLoadShip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipLoaderDialog dialog = new ShipLoaderDialog(shell); dialog.open(); } }); mntmConvertShp.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { File f = UIUtils.promptForLoadFile(shell, Superluminal.APP_NAME + " - Open .shp", prevShpPath, new String[] { "*.shp" }); if (f != null) { try { prevShpPath = f.getAbsolutePath(); Manager.loadShip(SHPUtils.loadShipSHP(f)); } catch (Exception ex) { log.error("Error occured while reading .shp file:", ex); String msg = "Superluminal was unable to open the legacy project file.\n\n" + "This can happen when the file is structurized in a way that the editor doesn't expect.\n" + "Posting the .shp file in the editor's thread at the FTL forums will help the editor's author fix the issue."; UIUtils.showWarningDialog(shell, null, msg); } } } }); mntmSaveShip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipContainer container = Manager.getCurrentShip(); if (!promptSaveShip(container, false)) log.trace("User exited save dialog, ship was not saved."); } }); mntmSaveShipAs.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipContainer container = Manager.getCurrentShip(); if (!promptSaveShip(container, true)) log.trace("User exited save dialog, ship was not saved."); } }); mntmModMan.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ModManagementDialog dialog = new ModManagementDialog(shell); dialog.open(); } }); mntmReloadDb.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { final Exception[] ex = new Exception[1]; ex[0] = null; UIUtils.showLoadDialog(shell, null, null, new Action() { public void execute() { log.debug("Reloading Database..."); try { Database db = Database.getInstance(); db.removeEntry(db.getCore()); DatabaseEntry[] entries = db.getEntries(); for (DatabaseEntry de : db.getEntries()) db.removeEntry(de); File datsDir = new File(Manager.resourcePath); File dataFile = new File(datsDir + "/data.dat"); File resourceFile = new File(datsDir + "/resource.dat"); FTLPack data = new FTLPack(dataFile, "r"); FTLPack resource = new FTLPack(resourceFile, "r"); db.loadCore(data, resource); db.getCore().load(); for (DatabaseEntry de : entries) { // Need to reopen the entries, since they were closed during removal db.addEntry(new DatabaseEntry(de.getFile())); } db.cacheAnimations(); } catch (Exception e) { ex[0] = e; } } }); if (ex[0] != null) { log.error("An error has occured while reloading the database.", ex[0]); String msg = "An error has occured while reloading the Database:\n" + ex[0].getClass().getSimpleName() + ": " + ex[0].getMessage() + "\n\n" + "Check the log for details."; UIUtils.showErrorDialog(null, null, msg); } } }); mntmCloseShip.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.closeShip(); } }); mntmUndo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (Manager.canUndo()) Manager.undo(); } }); mntmRedo.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (Manager.canRedo()) Manager.redo(); } }); mntmResetLinks.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().getShipController().getGameObject().resetDoorLinks(); updateSidebarContent(); } }); mntmOptimalOffset.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ShipContainer container = Manager.getCurrentShip(); ShipController shipC = container.getShipController(); Point offset = container.findOptimalThickOffset(); Point fineOffset = container.findOptimalFineOffset(); UndoableOffsetsEdit edit = new UndoableOffsetsEdit(container); edit.setOld(new Tuple<Point, Point>(container.getShipOffset(), container.getShipFineOffset())); shipC.select(); container.setShipFineOffset(fineOffset.x, fineOffset.y); container.setShipOffset(offset.x, offset.y); shipC.updateProps(); // Don't deselect if it was actually selected by the user if (Manager.getSelected() != shipC) shipC.deselect(); edit.setCurrent(new Tuple<Point, Point>(container.getShipOffset(), container.getShipFineOffset())); container.postEdit(edit); } }); mntmGenerateFloor.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { final ShipContainer container = Manager.getCurrentShip(); if (container.getRoomControllers().length == 0) { UIUtils.showInfoDialog(null, null, "Unable to generate floor image, because the ship has no rooms."); return; } FloorgenDialog fd = new FloorgenDialog(shell); FloorImageFactory fif = fd.open(); if (fif == null) return; UndoablePropertyEdit<String> edit = new UndoablePropertyEdit<String>(container) { public void callback(String arg) { container.setImage(Images.FLOOR, arg); updateSidebarContent(); } @Override public String getPresentationName() { return "generate floor image"; } }; edit.setOld(container.getImage(Images.FLOOR)); Manager.getCurrentShip().generateFloorImage(fif); edit.setCurrent(container.getImage(Images.FLOOR)); if (!edit.isValuesEqual()) Manager.postEdit(edit); } }); mntmDelete.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { AbstractController selected = Manager.getSelected(); if (selected != null) { try { int index = -1; if (selected instanceof RoomController) { index = ((RoomController) selected).getId(); } else if (selected instanceof DoorController) { index = Utils.indexOf(Manager.getCurrentShip().getDoorControllers(), selected); } else if (selected instanceof MountController) { index = ((MountController) selected).getId(); } else if (selected instanceof GibController) { index = ((GibController) selected).getId(); } Manager.getCurrentShip().delete(selected); selected.redraw(); Manager.setSelected(null); Manager.getCurrentShip().postEdit(new UndoableDeleteEdit(selected, index)); } catch (NotDeletableException ex) { log.trace("Selected object is not deletable: " + selected.getClass().getSimpleName()); } } } }); mntmSettings.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { SettingsDialog dialog = new SettingsDialog(shell); dialog.open(); } }); mntmZoom.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { ZoomWindow zoom = ZoomWindow.getInstance(); if (zoom == null) zoom = new ZoomWindow(shell, canvas); zoom.open(); } }); mntmGrid.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Grid.getInstance().setVisible(mntmGrid.getSelection()); } }); mntmHangar.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setHangarVisible(mntmHangar.getSelection()); } }); mntmShowAnchor.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setAnchorVisible(mntmShowAnchor.getSelection()); } }); mntmShowMounts.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setMountsVisible(mntmShowMounts.getSelection()); } }); mntmShowRooms.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setRoomsVisible(mntmShowRooms.getSelection()); } }); mntmShowDoors.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setDoorsVisible(mntmShowDoors.getSelection()); } }); mntmShowStations.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setStationsVisible(mntmShowStations.getSelection()); } }); mntmShowHull.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { AbstractController ac = Manager.getCurrentShip().getImageController(Images.HULL); if (ac.isSelected()) Manager.setSelected(null); ac.setVisible(mntmShowHull.getSelection()); } }); mntmShowFloor.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { AbstractController ac = Manager.getCurrentShip().getImageController(Images.FLOOR); if (ac.isSelected()) Manager.setSelected(null); ac.setVisible(mntmShowFloor.getSelection()); } }); mntmShowShield.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { AbstractController ac = Manager.getCurrentShip().getImageController(Images.SHIELD); if (ac.isSelected()) Manager.setSelected(null); ac.setVisible(mntmShowShield.getSelection()); } }); mntmShowGibs.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Manager.getCurrentShip().setGibsVisible(mntmShowGibs.getSelection()); } }); mntmAbout.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { String msg = Superluminal.APP_NAME + " - a ship editor for FTL: Faster Than Light\n" + "Version " + Superluminal.APP_VERSION + "\n\n" + "Created by " + Superluminal.APP_AUTHOR + "\n"; AboutDialog aboutDialog = new AboutDialog(shell); aboutDialog.setMessage(msg); try { aboutDialog.setLink(new URL(Superluminal.APP_FORUM_URL), "Editor's thread at the official FTL forums"); } catch (MalformedURLException ex) { } aboutDialog.open(); } }); mntmUpdate.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Superluminal.checkForUpdates(true); // Manually checking for updates } }); Transfer[] dropTypes = new Transfer[] { FileTransfer.getInstance() }; dropTarget = new DropTarget(shell, DND.DROP_MOVE | DND.DROP_DEFAULT); dropTarget.setTransfer(dropTypes); dropTarget.addDropListener(new DropTargetAdapter() { @Override public void dragOver(DropTargetEvent e) { e.detail = DND.DROP_MOVE; e.feedback = DND.FEEDBACK_EXPAND | DND.FEEDBACK_SCROLL; Object object = FileTransfer.getInstance().nativeToJava(e.currentDataType); if (object instanceof String[]) { String fileList[] = (String[]) object; for (String path : fileList) { if (!path.endsWith(".ftl") && !path.endsWith(".zip")) { e.detail = DND.DROP_NONE; e.feedback = DND.FEEDBACK_NONE; break; } } } else { e.detail = DND.DROP_NONE; e.feedback = DND.FEEDBACK_NONE; } } @Override public void drop(DropTargetEvent e) { Object object = FileTransfer.getInstance().nativeToJava(e.currentDataType); if (Manager.getCurrentShip() == null) { if (object instanceof String[]) { final String fileList[] = (String[]) object; final Database db = Database.getInstance(); if (db == null) { String msg = "Database not found -- mods cannot be loaded.\n" + "In order to create database, point the editor to FTL installation."; UIUtils.showWarningDialog(shell, null, msg); return; } // Already ensured that all items are .ftl or .zip UIUtils.showLoadDialog(shell, null, "Loading mods, please wait...", new Action() { public void execute() { for (String path : fileList) { DatabaseEntry de; try { de = new DatabaseEntry(new File(path)); DatabaseEntry[] dbEntries = db.getEntries(); if (de == db.getCore()) continue; if (!Utils.contains(dbEntries, de)) db.addEntry(de); } catch (Exception ex) { log.warn(String.format("Could not create a database entry for file '%s': ", path), ex); } } db.cacheAnimations(); } }); } } else { String msg = "Mods cannot be loaded while a ship is opened.\n" + "Please close the ship, then try again."; UIUtils.showInfoDialog(shell, null, msg); } } }); canvas.addMouseListener(MouseInputDispatcher.getInstance()); canvas.addMouseMoveListener(MouseInputDispatcher.getInstance()); canvas.addMouseTrackListener(MouseInputDispatcher.getInstance()); canvas.addMouseMoveListener(new MouseMoveListener() { public void mouseMove(MouseEvent e) { int x = e.x, y = e.y; int gx = 0, gy = 0; if (Manager.mouseShipRelative && Manager.getCurrentShip() != null) { Point shipLoc = Manager.getCurrentShip().getShipController().getLocation(); x -= shipLoc.x; y -= shipLoc.y; if (x < 0) gx -= ShipContainer.CELL_SIZE; if (y < 0) gy -= ShipContainer.CELL_SIZE; } gx += x; gy += y; gx /= ShipContainer.CELL_SIZE; gy /= ShipContainer.CELL_SIZE; lblCursorLoc.setText(String.format("%-4s, %-4s | %-2s, %-2s", x, y, gx, gy)); } }); registerHotkeys(); updateHotkeyTooltips(); shell.setMinimumSize(SIDEBAR_MIN_WIDTH + CANVAS_MIN_SIZE, CANVAS_MIN_SIZE + toolContainer.getSize().y * 2); shell.setSize(Manager.windowSize); shell.setMaximized(Manager.startMaximised); shell.setMinimized(false); Grid.getInstance().updateBounds(canvas.getSize().x, canvas.getSize().y); sideContainer.setFocus(); enableTools(false); enableOptions(false); setVisibilityOptions(true); } public static EditorWindow getInstance() { return instance; } public void open() { shell.open(); } public void updateHotkeyTooltips() { Hotkey h = null; // File mntmNewShip.setText("New Ship"); h = Manager.getHotkey(Hotkeys.NEW_SHIP); if (h.isEnabled()) UIUtils.addHotkeyText(mntmNewShip, h.toString()); mntmLoadShip.setText("Load Ship"); h = Manager.getHotkey(Hotkeys.LOAD_SHIP); if (h.isEnabled()) UIUtils.addHotkeyText(mntmLoadShip, h.toString()); mntmConvertShp.setText("Open .shp"); h = Manager.getHotkey(Hotkeys.LOAD_LEGACY); if (h.isEnabled()) UIUtils.addHotkeyText(mntmConvertShp, h.toString()); mntmSaveShip.setText("Save Ship"); h = Manager.getHotkey(Hotkeys.SAVE_SHIP); if (h.isEnabled()) UIUtils.addHotkeyText(mntmSaveShip, h.toString()); mntmSaveShipAs.setText("Save Ship As"); h = Manager.getHotkey(Hotkeys.SAVE_SHIP_AS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmSaveShipAs, h.toString()); mntmModMan.setText("Mod Management"); h = Manager.getHotkey(Hotkeys.MANAGE_MOD); if (h.isEnabled()) UIUtils.addHotkeyText(mntmModMan, h.toString()); mntmCloseShip.setText("Close Ship"); h = Manager.getHotkey(Hotkeys.CLOSE_SHIP); if (h.isEnabled()) UIUtils.addHotkeyText(mntmCloseShip, h.toString()); // Edit mntmUndo.setText("Undo"); h = Manager.getHotkey(Hotkeys.UNDO); if (h.isEnabled()) UIUtils.addHotkeyText(mntmUndo, h.toString()); mntmRedo.setText("Redo"); h = Manager.getHotkey(Hotkeys.REDO); if (h.isEnabled()) UIUtils.addHotkeyText(mntmRedo, h.toString()); mntmDelete.setText("Delete"); h = Manager.getHotkey(Hotkeys.DELETE); if (h.isEnabled()) UIUtils.addHotkeyText(mntmDelete, h.toString()); mntmSettings.setText("Settings"); h = Manager.getHotkey(Hotkeys.SETTINGS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmSettings, h.toString()); // View mntmZoom.setText("Open Zoom Window"); h = Manager.getHotkey(Hotkeys.OPEN_ZOOM); if (h.isEnabled()) UIUtils.addHotkeyText(mntmZoom, h.toString()); mntmGrid.setText("Show Grid"); h = Manager.getHotkey(Hotkeys.TOGGLE_GRID); if (h.isEnabled()) UIUtils.addHotkeyText(mntmGrid, h.toString()); mntmHangar.setText("Show Hangar"); h = Manager.getHotkey(Hotkeys.TOGGLE_HANGAR); if (h.isEnabled()) UIUtils.addHotkeyText(mntmHangar, h.toString()); mntmShowAnchor.setText("Show Ship Origin"); h = Manager.getHotkey(Hotkeys.SHOW_ANCHOR); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowAnchor, h.toString()); mntmShowMounts.setText("Show Mounts"); h = Manager.getHotkey(Hotkeys.SHOW_MOUNTS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowMounts, h.toString()); mntmShowRooms.setText("Show Rooms"); h = Manager.getHotkey(Hotkeys.SHOW_ROOMS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowRooms, h.toString()); mntmShowDoors.setText("Show Doors"); h = Manager.getHotkey(Hotkeys.SHOW_DOORS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowDoors, h.toString()); mntmShowStations.setText("Show Stations"); h = Manager.getHotkey(Hotkeys.SHOW_STATIONS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowStations, h.toString()); mntmShowHull.setText("Show Hull"); h = Manager.getHotkey(Hotkeys.SHOW_HULL); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowHull, h.toString()); mntmShowFloor.setText("Show Floor"); h = Manager.getHotkey(Hotkeys.SHOW_FLOOR); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowFloor, h.toString()); mntmShowShield.setText("Show Shield"); h = Manager.getHotkey(Hotkeys.SHOW_SHIELD); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowShield, h.toString()); mntmShowGibs.setText("Show Gibs"); h = Manager.getHotkey(Hotkeys.SHOW_GIBS); if (h.isEnabled()) UIUtils.addHotkeyText(mntmShowGibs, h.toString()); // Tools h = Manager.getHotkey(Hotkeys.POINTER_TOOL); tltmPointer.setToolTipText("Manipulation Tool" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.CREATE_TOOL); tltmCreation.setToolTipText("Layout Creation Tool" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.IMAGES_TOOL); tltmImages.setToolTipText("Ship Images" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.PROPERTIES_TOOL); tltmProperties.setToolTipText("Ship Loadout and Properties" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.OVERVIEW_TOOL); tltmManager.setToolTipText("Overview" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.CLOAK); tltmCloak.setToolTipText("View Cloaked Appearance" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); h = Manager.getHotkey(Hotkeys.ANIMATE); tltmAnimate.setToolTipText("Animate Gibs" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); } public void updateUndoButtons() { mntmUndo.setEnabled(Manager.canUndo()); mntmUndo.setText(Manager.getUndoPresentationName()); Hotkey h = Manager.getHotkey(Hotkeys.UNDO); if (h.isEnabled()) UIUtils.addHotkeyText(mntmUndo, h.toString()); mntmRedo.setEnabled(Manager.canRedo()); mntmRedo.setText(Manager.getRedoPresentationName()); h = Manager.getHotkey(Hotkeys.REDO); if (h.isEnabled()) UIUtils.addHotkeyText(mntmRedo, h.toString()); } /** * Sets the control passed in argument as the content of the sidebar -- this is * what the scrolled area will display, and will scale the scroll against. */ public void setSidebarContent(Composite c) { sideContainer.setContent(c); c.pack(); int height = c.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; sideContainer.setMinHeight(height); } /** * Updates the minimum area of the sidebar, so that the scrollbar will appear when needed. */ public void updateSidebarScroll() { Control c = sideContainer.getContent(); if (c != null && !c.isDisposed()) { int height = c.computeSize(SWT.DEFAULT, SWT.DEFAULT).y; sideContainer.setMinHeight(height); } } /** * Do not use this method to <b>dispose</b> the content of the sidebar. * To do this, use {@link #disposeSidebarContent()}. * * @return the Composite currently held by the sidebar. */ public Composite getSidebarContent() { Control c = sideContainer.getContent(); if (c instanceof Composite) return (Composite) sideContainer.getContent(); else if (c != null) throw new IllegalStateException("Content of the sidebar is not a Composite: " + c.getClass().getSimpleName()); else return null; } public void updateSidebarContent() { Control c = sideContainer.getContent(); if (c != null) { if (c instanceof DataComposite) { ((DataComposite) c).reloadController(); ((DataComposite) c).updateData(); } } } public void setSidebarContentController(AbstractController controller) throws UnsupportedOperationException { Control c = sideContainer.getContent(); if (c != null) { if (c instanceof DataComposite) { ((DataComposite) c).setController(controller); } } } /** * Disposes the content of the sidebar, and sets the content to null.<br> * Prevents the editor from crashing when trying to change the sidebar positioning after closing a ship. */ public void disposeSidebarContent() { Control c = sideContainer.getContent(); if (c != null) c.dispose(); sideContainer.setContent(null); } /** @return the sidebar ScrolledComposite itself. */ public Composite getSidebarWidget() { return sideContainer; } public Shell getShell() { return shell; } /** * Checks whether the point is inside the canvas area -- eg. points non-negative coordinates * with values lesser than or equal to the canvas' dimensions (width and height) * * @return true if the point (relative to the canvas) is within the canvas bounds, false otherwise */ public boolean canvasContains(int x, int y) { Rectangle bounds = canvas.getBounds(); bounds.x = 0; bounds.y = 0; return bounds.contains(x, y); } /** Redraws the entire canvas area. */ public void canvasRedraw() { canvas.redraw(); } /** Redraws the part of canvas covered by the rectangle. */ public void canvasRedraw(Rectangle rect) { canvas.redraw(rect.x, rect.y, rect.width, rect.height, false); } /** Redraws the part of canvas covered by the rectangle. */ public void canvasRedraw(int x, int y, int w, int h) { canvas.redraw(x, y, w, h, false); } /** * Only to be used to programmatically select the tool, when the user doesn't directly click on the tool's icon. */ public void selectTool(Tools tool) { if (!isToolsEnabled()) return; for (ToolItem it : toolItemMap.values()) { if (toolItemMap.containsKey(tool)) it.setSelection(tool == (Tools) it.getData()); } } public void enableTools(boolean enable) { tltmPointer.setEnabled(enable); tltmCreation.setEnabled(enable); tltmImages.setEnabled(enable); tltmProperties.setEnabled(enable); tltmManager.setEnabled(enable); ShipContainer c = Manager.getCurrentShip(); tltmAnimate.setEnabled(enable || (c != null && c.isGibAnimationInProgress())); tltmCloak.setEnabled(enable); if (!enable && tltmCloak.getSelection()) tltmCloak.setSelection(false); else if (enable && c.getImageController(Images.CLOAK).isVisible()) tltmCloak.setSelection(true); sideContainer.getVerticalBar().setEnabled(enable); } public boolean isToolsEnabled() { return tltmPointer.isEnabled(); } public void enableOptions(boolean enable) { // File mntmSaveShip.setEnabled(enable); mntmSaveShipAs.setEnabled(enable); mntmCloseShip.setEnabled(enable); // Edit updateUndoButtons(); mntmUndo.setEnabled(enable && Manager.canUndo()); mntmRedo.setEnabled(enable && Manager.canRedo()); mntmResetLinks.setEnabled(enable); mntmOptimalOffset.setEnabled(enable); mntmGenerateFloor.setEnabled(enable); mntmDelete.setEnabled(enable); // View mntmGrid.setEnabled(enable); mntmHangar.setEnabled(enable); mntmShowAnchor.setEnabled(enable); mntmShowMounts.setEnabled(enable); mntmShowRooms.setEnabled(enable); mntmShowDoors.setEnabled(enable); mntmShowStations.setEnabled(enable); mntmShowHull.setEnabled(enable); mntmShowFloor.setEnabled(enable); mntmShowShield.setEnabled(enable); mntmShowGibs.setEnabled(enable); // Mod management only available when a ship is not loaded ShipContainer c = Manager.getCurrentShip(); mntmModMan.setEnabled(!enable && c == null); mntmReloadDb.setEnabled(!enable && c == null); } public boolean isOptionsEnabled() { return mntmSaveShip.isEnabled(); } /** * Toggles all visibility-related options. */ public void setVisibilityOptions(boolean set) { ShipContainer container = Manager.getCurrentShip(); mntmHangar.setSelection(false); mntmShowAnchor.setSelection(set); mntmShowMounts.setSelection(set); mntmShowRooms.setSelection(set); mntmShowDoors.setSelection(set); mntmShowStations.setSelection(set); mntmShowHull.setSelection(set); mntmShowFloor.setSelection(set); mntmShowShield.setSelection(set); mntmShowGibs.setSelection(set); if (container != null) { container.setHangarVisible(false); container.setAnchorVisible(set); container.setMountsVisible(set); container.setRoomsVisible(set); container.setDoorsVisible(set); container.setStationsVisible(set); } } /** * Allows to put the editor in a completely non-interactable state, save for the menu options under "Help". */ public void setInteractable(boolean interactable) { enableTools(interactable); enableOptions(interactable); // Toggle widgets not covered by the two methods mntmNewShip.setEnabled(interactable); mntmLoadShip.setEnabled(interactable); mntmConvertShp.setEnabled(interactable); mntmSettings.setEnabled(interactable); // Toggle the overview window, if it is currently opened OverviewWindow w = OverviewWindow.getInstance(); if (!w.isDisposed()) w.setEnabled(interactable); // Toggle tool Manager.setSelected(null); if (interactable) { Manager.selectTool(Tools.POINTER); } else { Manager.selectTool(null); } // Select none gib prop controller ShipContainer container = Manager.getCurrentShip(); if (container != null && !interactable) { GibPropContainer propC = container.getGibContainer(); propC.showControls(PropControls.NONE); } } /** * @return true if the editor window controls the focus and should execute hotkey actions. */ public boolean isFocusControl() { Control c = UIUtils.getDisplay().getFocusControl(); boolean result = c != null && !(c.isEnabled() && (c instanceof Spinner || (c instanceof Text && ((Text) c).getEditable()))); if (result) { result &= OverviewWindow.getInstance() == null || !OverviewWindow.getInstance().isActive(); result &= ShipLoaderDialog.getInstance() == null || !ShipLoaderDialog.getInstance().isActive(); result &= SettingsDialog.getInstance() == null || !SettingsDialog.getInstance().isActive(); result &= NewShipDialog.getInstance() == null || !NewShipDialog.getInstance().isActive(); result &= SaveOptionsDialog.getInstance() == null || !SaveOptionsDialog.getInstance().isActive(); result &= ModManagementDialog.getInstance() == null || !ModManagementDialog.getInstance().isActive(); result &= AboutDialog.getInstance() == null || !AboutDialog.getInstance().isActive(); result &= AliasDialog.getInstance() == null || !AliasDialog.getInstance().isActive(); result &= !LoadingDialog.isActive(); result &= GlowSelectionDialog.getInstance() == null || !GlowSelectionDialog.getInstance().isActive(); result &= WeaponSelectionDialog.getInstance() == null || !WeaponSelectionDialog.getInstance().isActive(); result &= DroneSelectionDialog.getInstance() == null || !DroneSelectionDialog.getInstance().isActive(); result &= AugmentSelectionDialog.getInstance() == null || !AugmentSelectionDialog.getInstance().isActive(); } return result; } /** * @return true if the control got focus, and false if it was unable to. */ public boolean forceFocus() { return canvas.forceFocus(); } public boolean isImageDrawn(Images type) { switch (type) { case HULL: return mntmShowHull.getSelection(); case FLOOR: return mntmShowFloor.getSelection(); case SHIELD: return mntmShowShield.getSelection(); case CLOAK: return tltmCloak.getSelection(); default: return false; } } public void dispose() { Manager.unhookHotkeys(shell); Cache.checkInColor(this, canvasRGB); Grid.getInstance().dispose(); Cache.dispose(); shell.dispose(); eventHandler.dispose(); } public void addListener(int eventType, SLListener listener) { eventHandler.hook(eventType, listener); } public void removeListener(int eventType, SLListener listener) { eventHandler.unhook(eventType, listener); } /** * Refreshes the sidebar's layout. */ public void layoutSidebar() { sideContainer.setParent(shell); canvas.setParent(shell); if (Manager.sidebarOnRightSide) { canvas.setParent(editorContainer); sideContainer.setParent(editorContainer); } else { sideContainer.setParent(editorContainer); canvas.setParent(editorContainer); } editorContainer.layout(); } public boolean promptSaveShip(ShipContainer container, boolean prompt) { SaveOptions sop = container.getSaveOptions(); File file = sop.file; DatabaseEntry mod = sop.mod; if (file == null || prompt) { SaveOptionsDialog dialog = new SaveOptionsDialog(shell); SaveOptions so = dialog.open(); file = so.file; mod = so.mod; } if (file != null) // User could've aborted selection, which returns null. container.save(file, mod); return file != null; } private void registerHotkeys() { Hotkey h = null; // ====== Menu hotkeys // File h = Manager.getHotkey(Hotkeys.NEW_SHIP); h.addNotifyAction(mntmNewShip, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.LOAD_SHIP); h.addNotifyAction(mntmLoadShip, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SAVE_SHIP); h.addNotifyAction(mntmSaveShip, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SAVE_SHIP_AS); h.addNotifyAction(mntmSaveShipAs, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.LOAD_LEGACY); h.addNotifyAction(mntmConvertShp, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.MANAGE_MOD); h.addNotifyAction(mntmModMan, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.CLOSE_SHIP); h.addNotifyAction(mntmCloseShip, true); Manager.hookHotkey(shell, h); // Edit h = Manager.getHotkey(Hotkeys.UNDO); h.addNotifyAction(mntmUndo, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.REDO); h.addNotifyAction(mntmRedo, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SETTINGS); h.addNotifyAction(mntmSettings, true); Manager.hookHotkey(shell, h); // View h = Manager.getHotkey(Hotkeys.OPEN_ZOOM); h.addNotifyAndToggleAction(mntmZoom, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.TOGGLE_GRID); h.addNotifyAndToggleAction(mntmGrid, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.TOGGLE_HANGAR); h.addNotifyAndToggleAction(mntmHangar, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_ANCHOR); h.addNotifyAndToggleAction(mntmShowAnchor, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_MOUNTS); h.addNotifyAndToggleAction(mntmShowMounts, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_ROOMS); h.addNotifyAndToggleAction(mntmShowRooms, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_DOORS); h.addNotifyAndToggleAction(mntmShowDoors, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_STATIONS); h.addNotifyAndToggleAction(mntmShowStations, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_HULL); h.addNotifyAndToggleAction(mntmShowHull, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_FLOOR); h.addNotifyAndToggleAction(mntmShowFloor, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_SHIELD); h.addNotifyAndToggleAction(mntmShowShield, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.SHOW_GIBS); h.addNotifyAndToggleAction(mntmShowGibs, true); Manager.hookHotkey(shell, h); // ====== Tool hotkeys h = Manager.getHotkey(Hotkeys.POINTER_TOOL); h.addNotifyAction(tltmPointer, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.CREATE_TOOL); h.addNotifyAction(tltmCreation, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.IMAGES_TOOL); h.addNotifyAction(tltmImages, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.PROPERTIES_TOOL); h.addNotifyAction(tltmProperties, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.OVERVIEW_TOOL); h.addNotifyAction(tltmManager, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.CLOAK); h.addNotifyAndToggleAction(tltmCloak, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.ANIMATE); h.addNotifyAndToggleAction(tltmAnimate, true); Manager.hookHotkey(shell, h); // Creation Tool hotkeys h = Manager.getHotkey(Hotkeys.ROOM_TOOL); addCreateToolAction(h, Tools.ROOM); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.DOOR_TOOL); addCreateToolAction(h, Tools.DOOR); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.MOUNT_TOOL); addCreateToolAction(h, Tools.WEAPON); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.STATION_TOOL); addCreateToolAction(h, Tools.STATION); Manager.hookHotkey(shell, h); // ====== Tool-specific hotkeys h = Manager.getHotkey(Hotkeys.DELETE); h.addNotifyAction(mntmDelete, true); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.DEL); h.addNotifyAction(mntmDelete, true); Manager.hookHotkey(shell, h); h = Manager.getHotkey(Hotkeys.PIN); h.setOnPress(new Action() { public void execute() { if (Manager.getSelectedToolId() == Tools.POINTER) { AbstractController selected = Manager.getSelected(); if (selected != null) { selected.setPinned(!selected.isPinned()); updateSidebarContent(); } } } }); Manager.hookHotkey(shell, h); // Modifier hotkeys h = new Hotkey(); h.setKey(SWT.SHIFT); h.setShift(true); addModifierAction(h, Modifiers.SHIFT); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.CTRL); h.setCtrl(true); addModifierAction(h, Modifiers.CONTROL); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.ALT); h.setAlt(true); addModifierAction(h, Modifiers.ALT); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.COMMAND); h.setCommand(true); addModifierAction(h, Modifiers.COMMAND); Manager.hookHotkey(shell, h); // Arrow hotkeys h = new Hotkey(); h.setKey(SWT.ARROW_UP); addNudgeAction(h, 0, -1); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.ARROW_RIGHT); addNudgeAction(h, 1, 0); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.ARROW_DOWN); addNudgeAction(h, 0, 1); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setKey(SWT.ARROW_LEFT); addNudgeAction(h, -1, 0); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setShift(true); h.setKey(SWT.ARROW_UP); addNudgeAction(h, 0, -1); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setShift(true); h.setKey(SWT.ARROW_RIGHT); addNudgeAction(h, 1, 0); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setShift(true); h.setKey(SWT.ARROW_DOWN); addNudgeAction(h, 0, 1); Manager.hookHotkey(shell, h); h = new Hotkey(); h.setShift(true); h.setKey(SWT.ARROW_LEFT); addNudgeAction(h, -1, 0); Manager.hookHotkey(shell, h); } private void addNudgeAction(final Hotkey h, final int amountX, final int amountY) { h.setOnPress(new Action() { public void execute() { AbstractController selected = Manager.getSelected(); if (selected != null && !selected.isPinned() && selected.isLocModifiable()) { Point p = selected.getPresentedLocation(); Rectangle oldBounds = null; oldBounds = selected.getBounds(); int nudgeX = h.getShift() && selected.getPresentedFactor() == 1 ? Utils.sign(amountX) * ShipContainer.CELL_SIZE : amountX; int nudgeY = h.getShift() && selected.getPresentedFactor() == 1 ? Utils.sign(amountY) * ShipContainer.CELL_SIZE : amountY; selected.setPresentedLocation(p.x + nudgeX, p.y + nudgeY); selected.updateFollowOffset(); Manager.getCurrentShip().updateBoundingArea(); selected.updateView(); selected.redraw(); canvasRedraw(oldBounds); updateSidebarContent(); } } }); } private void addCreateToolAction(Hotkey h, final Tools tool) { h.setOnPress(new Action() { public void execute() { if (tltmCreation.isEnabled()) { if (!tltmCreation.getSelection()) tltmCreation.notifyListeners(SWT.Selection, null); CreationTool ctool = (CreationTool) Manager.getTool(Tools.CREATOR); ctool.selectSubtool(tool); } } }); } private void addModifierAction(Hotkey h, Modifiers modifier) { switch (modifier) { case SHIFT: h.setOnPress(new Action() { public void execute() { eventHandler.sendEvent(new SLModShiftEvent(EditorWindow.this, true)); } }); h.setOnRelease(new Action() { public void execute() { eventHandler.sendEvent(new SLModShiftEvent(EditorWindow.this, false)); } }); break; case CONTROL: h.setOnPress(new Action() { public void execute() { eventHandler.sendEvent(new SLModControlEvent(EditorWindow.this, true)); } }); h.setOnRelease(new Action() { public void execute() { eventHandler.sendEvent(new SLModControlEvent(EditorWindow.this, false)); } }); break; case ALT: h.setOnPress(new Action() { public void execute() { eventHandler.sendEvent(new SLModAltEvent(EditorWindow.this, true)); } }); h.setOnRelease(new Action() { public void execute() { eventHandler.sendEvent(new SLModAltEvent(EditorWindow.this, false)); } }); break; case COMMAND: h.setOnPress(new Action() { public void execute() { eventHandler.sendEvent(new SLModCommandEvent(EditorWindow.this, true)); } }); h.setOnRelease(new Action() { public void execute() { eventHandler.sendEvent(new SLModCommandEvent(EditorWindow.this, false)); } }); break; default: break; } } /** * @param animate * if true, sets the button's appearance to 'ready-to-animate' state,<br> * if false, sets the button's appearance to 'stop animating' state. */ public void updateGibAnimationButton(boolean animate) { if (animate) { Cache.checkInImage(this, "cpath:/assets/stop.png"); tltmAnimate.setImage(Cache.checkOutImage(this, "cpath:/assets/play.png")); Hotkey h = Manager.getHotkey(Hotkeys.ANIMATE); tltmAnimate.setToolTipText("Animate Gibs" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); } else { Cache.checkInImage(this, "cpath:/assets/play.png"); tltmAnimate.setImage(Cache.checkOutImage(this, "cpath:/assets/stop.png")); Hotkey h = Manager.getHotkey(Hotkeys.ANIMATE); tltmAnimate.setToolTipText("Stop Gib Animation" + (h.isEnabled() ? String.format(" (%s)", h.toString()) : "")); } } }