Java tutorial
// The MIT License (MIT) // // Copyright (c) 2015, 2016 Arian Fornaris // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the // "Software"), to deal in the Software without restriction, including // without limitation the rights to use, copy, modify, merge, publish, // distribute, sublicense, and/or sell copies of the Software, and to permit // persons to whom the Software is furnished to do so, subject to the // following conditions: The above copyright notice and this permission // notice shall be included in all copies or substantial portions of the // Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE // USE OR OTHER DEALINGS IN THE SOFTWARE. package phasereditor.canvas.ui.editors; import static phasereditor.ui.PhaserEditorUI.swtRun; import java.beans.PropertyChangeEvent; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.nio.file.Files; import java.util.Arrays; import org.eclipse.core.commands.operations.IUndoContext; import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.Path; import org.eclipse.jface.action.Action; import org.eclipse.jface.action.GroupMarker; import org.eclipse.jface.action.MenuManager; import org.eclipse.jface.action.Separator; import org.eclipse.jface.action.ToolBarManager; import org.eclipse.jface.dialogs.MessageDialog; import org.eclipse.jface.util.LocalSelectionTransfer; import org.eclipse.jface.viewers.ISelection; import org.eclipse.jface.viewers.TreeViewer; import org.eclipse.jface.window.Window; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.SashForm; import org.eclipse.swt.dnd.DND; import org.eclipse.swt.dnd.DragSourceEvent; import org.eclipse.swt.dnd.DragSourceListener; import org.eclipse.swt.dnd.Transfer; import org.eclipse.swt.events.FocusEvent; import org.eclipse.swt.events.FocusListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Display; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.ToolBar; import org.eclipse.swt.widgets.Tree; import org.eclipse.ui.IEditorInput; import org.eclipse.ui.IEditorSite; import org.eclipse.ui.IFileEditorInput; import org.eclipse.ui.IMemento; import org.eclipse.ui.IPersistableEditor; import org.eclipse.ui.PartInitException; import org.eclipse.ui.contexts.IContextActivation; import org.eclipse.ui.contexts.IContextService; import org.eclipse.ui.dialogs.FilteredTree; import org.eclipse.ui.menus.CommandContributionItem; import org.eclipse.ui.menus.CommandContributionItemParameter; import org.eclipse.ui.part.EditorPart; import org.eclipse.ui.part.FileEditorInput; import org.json.JSONException; import org.json.JSONObject; import org.json.JSONTokener; import javafx.geometry.Point2D; import phasereditor.canvas.core.JSCodeGenerator; import phasereditor.canvas.core.WorldModel; import phasereditor.canvas.ui.editors.behaviors.ZoomBehavior; import phasereditor.canvas.ui.editors.grid.PGrid; import phasereditor.canvas.ui.editors.operations.ChangeSettingsOperation; import phasereditor.canvas.ui.editors.operations.CompositeOperation; import phasereditor.canvas.ui.editors.palette.PaletteComp; import phasereditor.ui.EditorSharedImages; import phasereditor.ui.IEditorSharedImages; import phasereditor.ui.PatternFilter2; /** * @author arian * */ public class CanvasEditor extends EditorPart implements IResourceChangeListener, IPersistableEditor, IEditorSharedImages { private static final String PALETTE_CONTEXT_ID = "phasereditor.canvas.ui.palettecontext"; public final static String ID = "phasereditor.canvas.ui.editors.canvas"; public final static String NODES_CONTEXT_ID = "phasereditor.canvas.ui.nodescontext"; protected static final String SCENE_CONTEXT_ID = "phasereditor.canvas.ui.scenecontext"; protected static final String EDITOR_CONTEXT_ID = "phasereditor.canvas.ui.any"; public static final IUndoContext UNDO_CONTEXT = new IUndoContext() { @Override public boolean matches(IUndoContext context) { return context == this; } @Override public String getLabel() { return "CANVAS_CONTEXT"; } }; private ObjectCanvas _canvas; private CanvasEditorModel _model; private PGrid _grid; private SashForm _leftSashForm; private FilteredTree _outlineTree; private ToolBarManager _toolBarManager; private SashForm _mainSashForm; private PaletteComp _paletteComp; protected IContextActivation _paletteContext; private Action _showPaletteAction; private Action _showSidePaneAction; public CanvasEditor() { } @SuppressWarnings({ "rawtypes", "unchecked" }) @Override public Object getAdapter(Class adapter) { if (adapter == CanvasEditor.class) { return this; } return super.getAdapter(adapter); } @SuppressWarnings("unused") public void modelDirtyChanged(PropertyChangeEvent event) { firePropertyChange(PROP_DIRTY); } @Override public void doSave(IProgressMonitor monitor) { try { IFileEditorInput input = (IFileEditorInput) getEditorInput(); JSONObject data = new JSONObject(); _model.write(data); input.getFile().setContents(new ByteArrayInputStream(data.toString(2).getBytes()), true, false, monitor); if (getCanvas().getSettingsModel().isGenerateOnSave()) { generateCode(); } _model.getWorld().setDirty(false); firePropertyChange(PROP_DIRTY); } catch (JSONException | CoreException e) { e.printStackTrace(); throw new RuntimeException(e); } } @Override protected void setInput(IEditorInput input) { super.setInput(input); IFileEditorInput fileInput = (IFileEditorInput) input; IFile file = fileInput.getFile(); try (InputStream contents = file.getContents();) { JSONObject data = new JSONObject(new JSONTokener(contents)); _model = new CanvasEditorModel(file); try { _model.read(data); } catch (Exception e) { e.printStackTrace(); _model = new CanvasEditorModel(file); Display.getDefault().asyncExec(new Runnable() { @Override public void run() { Shell shell = Display.getDefault().getActiveShell(); MessageDialog.openError(shell, "Error", "The scene data cannot ve loaded.\n" + e.getMessage()); } }); } _model.getWorld().setFile(file); _model.getWorld().addPropertyChangeListener(WorldModel.PROP_STRUCTURE, arg -> { firePropertyChange(PROP_DIRTY); }); swtRun(this::updateTitle); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } @Override public void doSaveAs() { // nothing } @Override public void init(IEditorSite site, IEditorInput input) throws PartInitException { setSite(site); setInput(input); } @Override public boolean isDirty() { return _model.getWorld().isDirty(); } @Override public boolean isSaveAsAllowed() { return false; } @SuppressWarnings("unused") @Override public void createPartControl(Composite parent1) { GridLayout gl_parent1 = new GridLayout(1, false); gl_parent1.marginWidth = 0; gl_parent1.marginHeight = 0; parent1.setLayout(gl_parent1); Composite parent = new Composite(parent1, SWT.NONE); parent.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); _toolBar = new ToolBar(parent, SWT.FLAT | SWT.RIGHT); _toolBar.setLayoutData(new GridData(SWT.CENTER, SWT.CENTER, false, false, 1, 1)); GridLayout gl_parent = new GridLayout(1, false); gl_parent.verticalSpacing = 0; gl_parent.horizontalSpacing = 0; gl_parent.marginWidth = 3; gl_parent.marginHeight = 3; parent.setLayout(gl_parent); _mainSashForm = new SashForm(parent, SWT.NONE); _mainSashForm.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); _leftSashForm = new SashForm(_mainSashForm, SWT.VERTICAL); _outlineTree = new FilteredTree(_leftSashForm, SWT.BORDER | SWT.MULTI, new PatternFilter2(), true); Tree tree = _outlineTree.getViewer().getTree(); _outlineTree.getViewer().setLabelProvider(new OutlineLabelProvider()); _outlineTree.getViewer().setContentProvider(new OutlineContentProvider()); _grid = new PGrid(_leftSashForm, SWT.NONE); _leftSashForm.setWeights(new int[] { 1, 1 }); _centerComposite = new Composite(_mainSashForm, SWT.NONE); GridLayout gl_composite = new GridLayout(2, false); gl_composite.marginHeight = 0; gl_composite.marginWidth = 0; gl_composite.horizontalSpacing = 0; gl_composite.verticalSpacing = 2; _centerComposite.setLayout(gl_composite); _canvas = new ObjectCanvas(_centerComposite, SWT.BORDER); _canvas.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, true, 1, 1)); _paletteComp = new PaletteComp(_centerComposite, SWT.NONE); GridData gd_paletteComp = new GridData(SWT.RIGHT, SWT.FILL, false, true, 1, 1); gd_paletteComp.widthHint = 80; _paletteComp.setLayoutData(gd_paletteComp); _mainSashForm.setWeights(new int[] { 1, 4 }); afterCreateWidgets(); } private void afterCreateWidgets() { // name ResourcesPlugin.getWorkspace().addResourceChangeListener(this); initPalette(); initCanvas(); initMenus(); initOutline(); restoreState(); initContexts(); } private void initPalette() { _paletteComp.setProject(getEditorInputFile().getProject()); } private void initContexts() { getContextService().activateContext(EDITOR_CONTEXT_ID); _canvas.addFocusListener(new FocusListener() { private IContextActivation _sceneContext; @Override public void focusLost(FocusEvent e) { getContextService().deactivateContext(_sceneContext); } @Override public void focusGained(FocusEvent e) { _sceneContext = getContextService().activateContext(SCENE_CONTEXT_ID); } }); FocusListener nodesContextHandler = new FocusListener() { private IContextActivation _nodesContext; @Override public void focusLost(FocusEvent e) { getContextService().deactivateContext(_nodesContext); } @Override public void focusGained(FocusEvent e) { _nodesContext = getContextService().activateContext(NODES_CONTEXT_ID); } }; _canvas.addFocusListener(nodesContextHandler); _outlineTree.getViewer().getControl().addFocusListener(nodesContextHandler); // palette _paletteComp.getViewer().getTable().addFocusListener(new FocusListener() { @Override public void focusLost(FocusEvent e) { getContextService().deactivateContext(_paletteContext); } @Override public void focusGained(FocusEvent e) { _paletteContext = getContextService().activateContext(PALETTE_CONTEXT_ID); } }); } private void initMenus() { createMenuManager(); createToolbarManager(); } private void initCanvas() { _canvas.init(this, _model, _grid, _outlineTree.getViewer(), _paletteComp); getEditorSite().setSelectionProvider(_canvas.getSelectionBehavior()); } private void restoreState() { if (_state != null) { _canvas.getZoomBehavior().setScale(_state.zoomScale); _canvas.getZoomBehavior().setTranslate(_state.translate); _mainSashForm.setWeights(_state.sashWights); if (_state.paletteData != null) { _paletteComp.updateFromJSON(new JSONObject(_state.paletteData)); _showPaletteAction.setChecked(_paletteComp.isPaletteVisible()); } if (!_state.showSidePane) { _showSidePaneAction.run(); } _showSidePaneAction.setChecked(_state.showSidePane); } } private void initOutline() { TreeViewer viewer = _outlineTree.getViewer(); viewer.setInput(_canvas); viewer.expandAll(); int operations = DND.DROP_DEFAULT | DND.DROP_MOVE; Transfer[] transfers = new Transfer[] { LocalSelectionTransfer.getTransfer() }; viewer.addDragSupport(operations, transfers, new DragSourceListener() { private ISelection _data; @Override public void dragStart(DragSourceEvent event) { _data = viewer.getSelection(); } @Override public void dragSetData(DragSourceEvent event) { event.data = _data; LocalSelectionTransfer.getTransfer().setSelection(_data); } @Override public void dragFinished(DragSourceEvent event) { // finished } }); viewer.addDropSupport(operations, transfers, new OutlineDropAdapter(this)); } public IContextService getContextService() { IContextService service = getSite().getService(IContextService.class); return service; } private void createMenuManager() { MenuManager manager = createContextMenu(); getEditorSite().registerContextMenu(manager, _canvas.getSelectionBehavior(), false); _canvas.setMenu(manager.createContextMenu(_canvas)); _outlineTree.getViewer().getControl().setMenu(manager.createContextMenu(_outlineTree)); } private void createToolbarManager() { _toolBarManager = new ToolBarManager(_toolBar); { _toolBarManager.add(simpleCommand("phasereditor.canvas.ui.zoomRestore", "zoom_default.png")); } { // depth commands _toolBarManager.add(new Separator()); String[] defs = { "riseToTop", "shape_move_front.png", "rise", "shape_move_forwards.png", "lower", "shape_move_backwards.png", "lowerBottom", "shape_move_back.png", "-", "-", "align.left", "shape_align_left.png", "align.right", "shape_align_right.png", "align.top", "shape_align_top.png", "align.bottom", "shape_align_bottom.png", "align.center", "shape_align_center.png", "align.middle", "shape_align_middle.png", }; defsCommands(defs); } { _toolBarManager.add(new Separator()); _toolBarManager.add(simpleCommand("phasereditor.canvas.ui.quickEdit", "shape_square_edit.png")); _toolBarManager.add(simpleCommand("phasereditor.canvas.ui.addsprite", "car_add.png")); } { _toolBarManager.add(new Separator()); _toolBarManager.add(simpleCommand("phasereditor.canvas.ui.quickOutline", "outline_co.png")); _showSidePaneAction = new Action("Toggle the side pane.", SWT.TOGGLE) { { setImageDescriptor(EditorSharedImages.getImageDescriptor(IMG_APPLICATION_SIDE_TREE)); } @SuppressWarnings("synthetic-access") @Override public void run() { if (_mainSashForm.getMaximizedControl() == _centerComposite) { _mainSashForm.setMaximizedControl(null); } else { _mainSashForm.setMaximizedControl(_centerComposite); } } }; _showSidePaneAction.setChecked(true); _toolBarManager.add(_showSidePaneAction); _showPaletteAction = new Action("Toggle the palette.", SWT.TOGGLE) { { setChecked(false); setImageDescriptor(EditorSharedImages.getImageDescriptor(IMG_PALETTE)); } @Override public void run() { boolean visible = getPalette().isPaletteVisible(); getPalette().setPaletteVisble(!visible); } }; getPalette().setPaletteVisble(_showPaletteAction.isChecked()); _toolBarManager.add(_showPaletteAction); } { _toolBarManager.add(new Separator()); _toolBarManager.add(new Action("Settings", EditorSharedImages.getImageDescriptor(IMG_SETTINGS)) { @Override public void run() { openDialogSettings(); } }); } { _toolBarManager.add(new Action("Generate code", EditorSharedImages.getImageDescriptor(IMG_BUILD)) { @Override public void run() { generateCode(); } }); _toolBarManager.add(simpleCommand("phasereditor.canvas.ui.showcode", "jcu_obj.gif")); } _toolBarManager.update(true); } public IFile getFileToGenerate() { WorldModel model = getCanvas().getWorldModel(); String fname = model.getClassName() + ".js"; return getEditorInputFile().getParent().getFile(new Path(fname)); } void generateCode() { JSCodeGenerator generator = new JSCodeGenerator(); try { WorldModel model = getCanvas().getWorldModel(); IFile file = getFileToGenerate(); String replace = null; if (file.exists()) { byte[] bytes = Files.readAllBytes(file.getLocation().makeAbsolute().toFile().toPath()); replace = new String(bytes); } String content = generator.generate(model, replace); ByteArrayInputStream stream = new ByteArrayInputStream(content.getBytes()); if (file.exists()) { file.setContents(stream, IResource.NONE, null); } else { file.create(stream, false, null); } file.refreshLocal(1, null); } catch (Exception e) { e.printStackTrace(); throw new RuntimeException(e); } } private void defsCommands(String[] defs) { for (int i = 0; i < defs.length; i += 2) { if (defs[i].equals("-")) { _toolBarManager.add(new Separator()); continue; } _toolBarManager.add(simpleCommand("phasereditor.canvas.ui." + defs[i], defs[i + 1])); } } private CommandContributionItem simpleCommand(String cmd, String imgname) { CommandContributionItemParameter p = new CommandContributionItemParameter(getSite(), null, cmd, SWT.PUSH); p.icon = EditorSharedImages.getImageDescriptor("icons/" + imgname); return new CommandContributionItem(p); } private static MenuManager createContextMenu() { MenuManager menuManager = new MenuManager(); menuManager.add(new GroupMarker("object")); return menuManager; } @Override public void setFocus() { _canvas.setFocus(); } public PaletteComp getPalette() { return _paletteComp; } public ObjectCanvas getCanvas() { return _canvas; } public TreeViewer getOutline() { return _outlineTree.getViewer(); } public PGrid getPropertyGrid() { return _grid; } @Override public void dispose() { ResourcesPlugin.getWorkspace().removeResourceChangeListener(this); _canvas.getUpdateBehavior().dispose(); super.dispose(); } /* * (non-Javadoc) * * @see * org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org. * eclipse.core.resources.IResourceChangeEvent) */ @Override public void resourceChanged(IResourceChangeEvent event) { try { IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); if (event.getDelta() == null) { return; } event.getDelta().accept(new IResourceDeltaVisitor() { @SuppressWarnings("synthetic-access") @Override public boolean visit(IResourceDelta delta) throws CoreException { IFile thisFile = getEditorInputFile(); IResource deltaFile = delta.getResource(); if (deltaFile.equals(thisFile)) { if (delta.getKind() == IResourceDelta.REMOVED) { IPath movedTo = delta.getMovedToPath(); if (movedTo == null) { // delete Display display = Display.getDefault(); display.asyncExec(new Runnable() { @Override public void run() { getSite().getPage().closeEditor(CanvasEditor.this, false); } }); } else { // rename IFile file = root.getFile(movedTo); CanvasEditor.super.setInput(new FileEditorInput(file)); getModel().getWorld().setFile(file); swtRun(CanvasEditor.this::updateTitle); } } } return true; } }); } catch (CoreException e) { throw new RuntimeException(e); } } public IFile getEditorInputFile() { return ((IFileEditorInput) getEditorInput()).getFile(); } public CanvasEditorModel getModel() { return _model; } protected void updateTitle() { setPartName(getEditorInputFile().getName()); firePropertyChange(PROP_TITLE); } @Override public void saveState(IMemento memento) { ZoomBehavior zoom = _canvas.getZoomBehavior(); memento.putFloat("canvas.zoom.scale", (float) zoom.getScale()); memento.putFloat("canvas.translate.x", (float) zoom.getTranslate().getX()); memento.putFloat("canvas.translate.y", (float) zoom.getTranslate().getY()); memento.putString("canvas.palette.data", _paletteComp.toJSON().toString()); int[] weights = _mainSashForm.getWeights(); memento.putString("canvas.sash.weights", Arrays.toString(weights)); memento.putBoolean("canvas.sidepane.show", _mainSashForm.getMaximizedControl() == null); } static class State { double zoomScale = 0; Point2D translate = new Point2D(0, 0); int[] sashWights = new int[] { 1, 5 }; String paletteData; boolean showSidePane; } private State _state; private ToolBar _toolBar; private Composite _centerComposite; @Override public void restoreState(IMemento memento) { try { _state = new State(); { Float scale = memento.getFloat("canvas.zoom.scale"); _state.zoomScale = scale == null ? 0 : scale.doubleValue(); } { Float x = memento.getFloat("canvas.translate.x"); Float y = memento.getFloat("canvas.translate.y"); _state.translate = new Point2D(x == null ? 50 : x.doubleValue(), y == null ? 50 : y.doubleValue()); } { String value = memento.getString("canvas.sash.weights"); if (value != null) { value = value.replace("[", "").replace("]", ""); String[] array = value.split(","); _state.sashWights = new int[array.length]; for (int i = 0; i < array.length; i++) { _state.sashWights[i] = Integer.parseInt(array[i].trim()); } } } { _state.paletteData = memento.getString("canvas.palette.data"); } { Boolean b = memento.getBoolean("canvas.sidepane.show"); _state.showSidePane = b != null && b.booleanValue(); } } catch (Exception e) { e.printStackTrace(); } } void openDialogSettings() { CanvasSettingsDialog dlg = new CanvasSettingsDialog(getSite().getShell()); JSONObject data = new JSONObject(); _model.getSettings().write(data); SceneSettings settings = new SceneSettings(data); dlg.setModel(settings); if (dlg.open() == Window.OK) { settings.write(data); getCanvas().getUpdateBehavior() .executeOperations(new CompositeOperation(new ChangeSettingsOperation(data))); } } }