livecanvas.mesheditor.MeshEditor.java Source code

Java tutorial

Introduction

Here is the source code for livecanvas.mesheditor.MeshEditor.java

Source

package livecanvas.mesheditor;

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FileDialog;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;

import javax.swing.BorderFactory;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JToggleButton;
import javax.swing.JToolBar;
import javax.swing.UIManager;

import livecanvas.AffineTransformPointerHandler;
import livecanvas.CanvasContainer;
import livecanvas.CanvasMesh;
import livecanvas.Mesh;
import livecanvas.Path;
import livecanvas.Perspective;
import livecanvas.PointerHandler;
import livecanvas.RenderImageTask;
import livecanvas.Settings;
import livecanvas.Settings.SettingsContainer;
import livecanvas.Tool;
import livecanvas.Utils;
import livecanvas.Utils.ButtonType;
import livecanvas.components.Layer;
import livecanvas.components.LayersView;
import livecanvas.components.ViewpointsView;
import livecanvas.image.RenderData;
import livecanvas.image.Style;

import org.json.JSONObject;

import common.typeutils.AutoPanel;
import common.typeutils.BooleanType;
import common.typeutils.DirectoryType;
import common.typeutils.EnumType;
import common.typeutils.IntegerType;
import common.typeutils.PropertyFactory;
import common.typeutils.StringType;

public class MeshEditor extends Perspective
        implements Tool.ToolContext, LayersView.Listener, ViewpointsView.Listener {
    private static final Class clazz = MeshEditor.class;

    private JToolBar toolBar;
    private JMenuBar menuBar;
    private RenderImageSettings renderImageSettings;
    private MeshEditorSettings settings;
    private Color selectedColor;
    private JToolBar tools;
    private CanvasMesh canvas;
    private CanvasContainer canvasContainer;
    private JPanel propertiesPanel;
    private JComboBox rendererSelect;
    private LayersView layersView;
    private ViewpointsView viewpointsView;

    private PointerHandler pointerHandler;

    public MeshEditor() {
        super("Mesh Editor", new ImageIcon(clazz.getResource("res/mesh_editor.png")), new BorderLayout());
        setPreferredSize(new Dimension(1024, 600));
        setBackground(Color.darkGray);

        tools = createTools();
        add(tools, BorderLayout.WEST);

        selectedColor = Color.black;
        JPanel east = new JPanel(new BorderLayout());
        east.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        Layer rootLayer = new Layer("ROOT");
        rootLayer.addSubLayer(new Layer("Layer1"));
        layersView = new LayersView(rootLayer);
        chain(layersView);
        layersView.addListener(this);
        east.add(layersView, BorderLayout.NORTH);
        propertiesPanel = new JPanel(new BorderLayout());
        east.add(propertiesPanel);
        viewpointsView = new ViewpointsView(layersView.getSelectedLayer());
        viewpointsView.addListener(this);
        east.add(viewpointsView, BorderLayout.SOUTH);
        add(east, BorderLayout.EAST);

        JPanel center = new JPanel(new BorderLayout());
        center.setBackground(Color.lightGray);
        canvas = new CanvasMesh(800, 600);
        canvas.setCurrLayer(getRootLayer());
        pointerHandler = new AffineTransformPointerHandler(canvas);
        canvas.setPointerHandler(pointerHandler);
        canvasContainer = new CanvasContainer(canvas);
        rootLayer.setCanvas(canvas);
        center.add(canvasContainer);

        renderImageSettings = new RenderImageSettings();
        settings = new MeshEditorSettings(canvas.getSettings(), layersView.getSettings(), renderImageSettings);

        add(center);
        layersView.layersList.setSelectedIndex(1);
        setSelectedToolType(TOOLS_BRUSH);
    }

    @Override
    public boolean handleEvent(ActionEvent e) {
        switch (Integer.parseInt(e.getActionCommand())) {
        case EXIT:
            exit();
            break;
        case NEW:
            clear();
            break;
        case OPEN:
            open(null);
            break;
        case SAVE:
            save(null);
            break;
        case SETTINGS:
            showSettingsDialog();
            break;
        case SEE_THROUGH:
            toggleOnionSkin();
            break;
        case SHOW_MESH:
            toggleShowMesh();
            break;
        case RENDER_SETTINGS:
            renderSettings();
            break;
        case RENDER:
            showImage(render());
            break;
        case TOOLS_BRUSH:
            setSelectedToolType(TOOLS_BRUSH);
            break;
        case TOOLS_PEN:
            setSelectedToolType(TOOLS_PEN);
            break;
        case TOOLS_MAGICWAND:
            setSelectedToolType(TOOLS_MAGICWAND);
            break;
        case TOOLS_SETCONTROLPOINTS:
            setSelectedToolType(TOOLS_SETCONTROLPOINTS);
            break;
        case TOOLS_POINTER:
            setSelectedToolType(TOOLS_POINTER);
            break;
        case TOOLS_PANZOOM:
            setSelectedToolType(TOOLS_PANZOOM);
            break;
        default:
            return false;
        }
        repaint();
        return true;
    }

    public Layer getRootLayer() {
        return layersView.getRootLayer();
    }

    public JToolBar getToolBar() {
        if (toolBar == null) {
            toolBar = createToolBar();
        }
        return toolBar;
    }

    public JMenuBar getMenuBar() {
        if (menuBar == null) {
            menuBar = createMenuBar();
        }
        return menuBar;
    }

    public MeshEditorSettings getSettings() {
        return settings;
    }

    public Color getSelectedColor() {
        return selectedColor;
    }

    @Override
    public void layerSelectionChanged(Layer selectedLayer) {
        canvas.setCurrLayer(selectedLayer);
        viewpointsView.layerChanged(selectedLayer);
        repaint();
    }

    @Override
    public void viewpointChanged(int vx, int vy) {
        layersView.getSelectedLayer().setCurrViewpoint(vx, vy);
        repaint();
    }

    private JToolBar createToolBar() {
        JToolBar toolbar = new JToolBar();
        JButton button;
        toolbar.add(Utils.createToolBarButton("New mesh", new ImageIcon(clazz.getResource("res/new.png")),
                ButtonType.ICON_ONLY, NEW, "New", this));
        toolbar.add(Utils.createToolBarButton("Open", new ImageIcon(clazz.getResource("res/open.png")),
                ButtonType.ICON_ONLY, OPEN, "Open a saved mesh", this));
        toolbar.add(Utils.createToolBarButton("Save", new ImageIcon(clazz.getResource("res/save.png")),
                ButtonType.ICON_ONLY, SAVE, "Save mesh", this));
        toolbar.addSeparator();
        toolbar.add(Utils.createToolBarButton("Cut", new ImageIcon(clazz.getResource("res/cut.png")),
                ButtonType.ICON_ONLY, CUT, "Cut selection to clipboard", this));
        toolbar.add(Utils.createToolBarButton("Copy", new ImageIcon(clazz.getResource("res/copy.png")),
                ButtonType.ICON_ONLY, COPY, "Copy selection to clipboard", this));
        toolbar.add(Utils.createToolBarButton("Paste", new ImageIcon(clazz.getResource("res/paste.png")),
                ButtonType.ICON_ONLY, PASTE, "Paste content from clipboard", this));
        toolbar.addSeparator();
        toolbar.add(Utils.createToolBarButton("Undo", new ImageIcon(clazz.getResource("res/undo.png")),
                ButtonType.ICON_ONLY, UNDO, "Undo last action", this));
        toolbar.add(Utils.createToolBarButton("Redo", new ImageIcon(clazz.getResource("res/redo.png")),
                ButtonType.ICON_ONLY, REDO, "Redo last undone action", this));
        toolbar.addSeparator();
        JPanel rendererSelectContainer = new JPanel();
        rendererSelectContainer.setOpaque(false);
        rendererSelectContainer.setMaximumSize(new Dimension(150, 40));
        rendererSelectContainer.add(rendererSelect = new JComboBox(Style.Styles));
        rendererSelect.setSelectedIndex(Style.Styles.length - 1);
        toolbar.add(rendererSelectContainer);
        toolbar.add(Utils.createToolBarButton("Render Settings",
                new ImageIcon(clazz.getResource("res/render_settings.png")), ButtonType.ICON_ONLY, RENDER_SETTINGS,
                "Render Settings", this));
        toolbar.add(Utils.createToolBarButton("Render", new ImageIcon(clazz.getResource("res/render.png")),
                ButtonType.ICON_ONLY, RENDER, "Render mesh using current style", this));
        return toolbar;
    }

    private JToolBar createTools() {
        JToolBar toolbar = new JToolBar(JToolBar.VERTICAL);
        ButtonGroup toolsBg = new ButtonGroup();
        JToggleButton toolsButton;
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("Brush",
                new ImageIcon(clazz.getResource("res/brush.png")), ButtonType.ICON_ONLY, TOOLS_BRUSH, "Brush",
                toolsBg, this));
        toolsButton.setSelected(true);
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("Pen",
                new ImageIcon(clazz.getResource("res/pen.png")), ButtonType.ICON_ONLY, TOOLS_PEN, "Pen", toolsBg,
                this));
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("MagicWand",
                new ImageIcon(clazz.getResource("res/wand.png")), ButtonType.ICON_ONLY, TOOLS_MAGICWAND,
                "Magic Wand", toolsBg, this));
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("SetControlPoints",
                new ImageIcon(clazz.getResource("res/set_controls.png")), ButtonType.ICON_ONLY,
                TOOLS_SETCONTROLPOINTS, "Set Control Points", toolsBg, this));
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("Pointer",
                new ImageIcon(clazz.getResource("res/pointer.png")), ButtonType.ICON_ONLY, TOOLS_POINTER, "Pointer",
                toolsBg, this));
        toolbar.add(toolsButton = Utils.createToolBarToggleButton("PanZoom",
                new ImageIcon(clazz.getResource("res/panzoom.png")), ButtonType.ICON_ONLY, TOOLS_PANZOOM,
                "Pan or Zoom Canvas", toolsBg, this));
        // toolsButton.setSelected(true);
        return toolbar;
    }

    public JMenuBar createMenuBar() {
        JMenuBar menuBar = new JMenuBar();
        JMenu menu, subMenu;
        JMenuItem menuItem;
        menu = new JMenu("File");
        menu.setMnemonic(KeyEvent.VK_F);
        menu.add(menuItem = Utils.createMenuItem("New", NEW, KeyEvent.VK_N, "ctrl N", this));
        menu.add(menuItem = Utils.createMenuItem("Open...", OPEN, KeyEvent.VK_O, "ctrl O", this));
        menu.add(menuItem = Utils.createMenuItem("Save", SAVE, KeyEvent.VK_S, "ctrl S", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Exit", EXIT, KeyEvent.VK_X, "", this));
        menuBar.add(menu);
        menu = new JMenu("Edit");
        menu.setMnemonic(KeyEvent.VK_E);
        menu.add(menuItem = Utils.createMenuItem("Undo", UNDO, KeyEvent.VK_U, "ctrl Z", this));
        menu.add(menuItem = Utils.createMenuItem("Redo", REDO, KeyEvent.VK_R, "ctrl Y", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Cut", CUT, KeyEvent.VK_T, "ctrl X", this));
        menu.add(menuItem = Utils.createMenuItem("Copy", COPY, KeyEvent.VK_C, "ctrl C", this));
        menu.add(menuItem = Utils.createMenuItem("Paste", PASTE, KeyEvent.VK_P, "ctrl V", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Select All", SELECT_ALL, KeyEvent.VK_A, "ctrl A", this));
        menu.add(menuItem = Utils.createMenuItem("Invert Selection", INVERT_SELECTION, 0, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Settings...", SETTINGS, KeyEvent.VK_S, "", this));
        menuBar.add(menu);
        menu = new JMenu("Tools");
        menu.setMnemonic(KeyEvent.VK_T);
        menu.add(menuItem = Utils.createMenuItem("Brush", TOOLS_BRUSH, KeyEvent.VK_B, "B", this));
        menu.add(menuItem = Utils.createMenuItem("Pencil", TOOLS_PEN, KeyEvent.VK_N, "N", this));
        menu.add(menuItem = Utils.createMenuItem("Magic Wand", TOOLS_MAGICWAND, KeyEvent.VK_W, "W", this));
        menu.add(menuItem = Utils.createMenuItem("Set Control Points", TOOLS_SETCONTROLPOINTS, KeyEvent.VK_C, "C",
                this));
        menu.add(menuItem = Utils.createMenuItem("Pointer", TOOLS_POINTER, KeyEvent.VK_P, "P", this));
        menu.add(menuItem = Utils.createMenuItem("Pan / Zoom", TOOLS_PANZOOM, KeyEvent.VK_Z, "Z", this));
        menuBar.add(menu);
        menu = new JMenu("Layers");
        menu.setMnemonic(KeyEvent.VK_L);
        menu.add(menuItem = Utils.createMenuItem("Add Layer...", ADD_LAYER, KeyEvent.VK_A, "", this));
        menu.add(menuItem = Utils.createMenuItem("Remove", REMOVE_LAYER, KeyEvent.VK_R, "", this));
        menu.add(menuItem = Utils.createMenuItem("Duplicate", DUPLICATE_LAYER, KeyEvent.VK_C, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Move Up", MOVEUP_LAYER, KeyEvent.VK_U, "", this));
        menu.add(menuItem = Utils.createMenuItem("Move Down", MOVEDOWN_LAYER, KeyEvent.VK_D, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Reparent Layer...", REPARENT_LAYER, KeyEvent.VK_R, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Group Layers", GROUP_LAYERS, KeyEvent.VK_G, "", this));
        menu.add(menuItem = Utils.createMenuItem("Ungroup Layer", UNGROUP_LAYER, KeyEvent.VK_N, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Join Layers", JOIN_LAYERS, KeyEvent.VK_J, "", this));
        menu.add(menuItem = Utils.createMenuItem("Intersect", INTERSECT_LAYERS, KeyEvent.VK_I, "", this));
        menu.add(menuItem = Utils.createMenuItem("Subtract", SUBTRACT_LAYERS, KeyEvent.VK_S, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Rename...", RENAME_LAYER, KeyEvent.VK_E, "", this));
        menu.addSeparator();
        subMenu = new JMenu("Background Reference");
        subMenu.add(menuItem = Utils.createMenuItem("Set...", BGREF_SET, KeyEvent.VK_S, "", this));
        subMenu.add(menuItem = Utils.createMenuItem("Remove", BGREF_REMOVE, KeyEvent.VK_R, "", this));
        menu.add(subMenu);
        menuBar.add(menu);
        menu.addSeparator();
        menu.add(menuItem = Utils.createMenuItem("Create Mesh Grid", CREATE_MESHGRID, KeyEvent.VK_M, "", this));
        menu.addSeparator();
        menu.add(menuItem = Utils.createCheckBoxMenuItem("See Through", SEE_THROUGH, KeyEvent.VK_T, "", this));
        menuItem.setSelected(true);
        menu.add(menuItem = Utils.createCheckBoxMenuItem("Show Mesh", SHOW_MESH, KeyEvent.VK_M, "", this));
        menuItem.setSelected(true);
        return menuBar;
    }

    private void setSelectedToolType(int type) {
        Tool prev, next;
        prev = canvas.getSelectedTool();
        canvas.setSelectedToolType(type);
        next = canvas.getSelectedTool();
        JToggleButton toolBtn;
        if (!next.onSelected(this)) {
            toolBtn = findToolButtonByName(prev.name);
            toolBtn.setSelected(true);
            canvas.setSelectedTool(prev);
            return;
        } else if (prev != null) {
            prev.onDeselected(this);
        }
        if (!(toolBtn = findToolButtonByName(next.name)).isSelected()) {
            toolBtn.setSelected(true);
        }
        propertiesPanel.removeAll();
        propertiesPanel.add(new AutoPanel(PropertyFactory.createProperties(next)));
        propertiesPanel.revalidate();
        propertiesPanel.repaint();
    }

    private JToggleButton findToolButtonByName(String name) {
        List<JToggleButton> toolBtns = Utils.findComponentsOfType(tools, JToggleButton.class);
        for (JToggleButton toolBtn : toolBtns) {
            if (toolBtn.getName().equals(name)) {
                return toolBtn;
            }
        }
        return null;
    }

    private void open(File file) {
        if (file == null) {
            FileDialog fd = new FileDialog(JOptionPane.getFrameForComponent(MeshEditor.this), "Load");
            fd.setVisible(true);
            String file_str = fd.getFile();
            if (file_str == null) {
                return;
            }
            file = new File(fd.getDirectory() + "/" + file_str);
        }
        try {
            StringWriter out = new StringWriter();
            InputStreamReader in = new InputStreamReader(new FileInputStream(file));
            Utils.copy(in, out);
            in.close();
            out.close();
            clear();
            JSONObject doc = new JSONObject(out.toString());
            Layer rootLayer = Layer.fromJSON(doc.getJSONObject("rootLayer"));
            layersView.setRootLayer(rootLayer);
            rootLayer.setCanvas(canvas);
            for (Layer layer : rootLayer.getSubLayersRecursively()) {
                layer.setCanvas(canvas);
            }
            JSONObject canvasJSON = doc.optJSONObject("canvas");
            if (canvasJSON != null) {
                canvas.fromJSON(canvasJSON);
            }
            canvas.setCurrLayer(rootLayer);
        } catch (Exception e1) {
            e1.printStackTrace();
            String msg = "An error occurred while trying to load.";
            JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(MeshEditor.this), msg, "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
        repaint();
    }

    private boolean canSave() {
        return canvas.canSave();
    }

    private void save(File file) {
        if (!canSave()) {
            return;
        }
        if (file == null) {
            FileDialog fd = new FileDialog(JOptionPane.getFrameForComponent(MeshEditor.this), "Save");
            fd.setVisible(true);
            String file_str = fd.getFile();
            if (file_str == null) {
                return;
            }
            file = new File(fd.getDirectory() + "/" + file_str);
        }
        try {
            Layer rootLayer = layersView.getRootLayer();
            for (Layer l : rootLayer.getSubLayersRecursively()) {
                Path path = l.getPath();
                if (path.isFinalized()) {
                    Mesh mesh = path.getMesh();
                    if (mesh.getControlPointsCount() < 2) {
                        String msg = "At least one mesh has less than 2 control points.\n"
                                + "You may not be able to use it for animation. Do you\n"
                                + "still want to continue?";
                        if (JOptionPane.showConfirmDialog(this, msg, "Warning", JOptionPane.YES_NO_OPTION,
                                JOptionPane.WARNING_MESSAGE) != JOptionPane.YES_OPTION) {
                            return;
                        } else {
                            break;
                        }
                    }
                }
            }
            PrintWriter out = new PrintWriter(new FileOutputStream(file));
            JSONObject doc = new JSONObject();
            doc.put("rootLayer", rootLayer.toJSON());
            doc.put("canvas", canvas.toJSON());
            doc.write(out);
            out.close();
        } catch (Exception e1) {
            e1.printStackTrace();
            String msg = "An error occurred while trying to load.";
            JOptionPane.showMessageDialog(JOptionPane.getFrameForComponent(MeshEditor.this), msg, "Error",
                    JOptionPane.ERROR_MESSAGE);
        }
        repaint();
    }

    private void toggleOnionSkin() {
        canvas.setSeeThrough(!canvas.isSeeThrough());
    }

    private void toggleShowMesh() {
        canvas.setShowMesh(!canvas.isShowMesh());
    }

    private BufferedImage render() {
        RenderData data = new RenderData();
        Layer rootLayer = layersView.getRootLayer();
        data.canvasSize = new Dimension(canvas.getWidth(), canvas.getHeight());
        data.layer = rootLayer;
        Style style = (Style) rendererSelect.getSelectedItem();
        return RenderImageTask.render(this, style, data, renderImageSettings);
    }

    private void renderSettings() {
        ((Style) rendererSelect.getSelectedItem()).showSettings(this);
    }

    private void showImage(Image image) {
        if (image == null) {
            return;
        }
        JDialog d = new JDialog(JOptionPane.getFrameForComponent(MeshEditor.this), "Image View", true);
        d.setContentPane(new JLabel(new ImageIcon(image)));
        d.pack();
        d.setLocationRelativeTo(d.getParent());
        d.setVisible(true);
        d.dispose();
    }

    private void clear() {
        canvas.clear();
    }

    private void exit() {
        if (JOptionPane.showConfirmDialog(MeshEditor.this, "Are you sure you want to exit?", "Exit",
                JOptionPane.YES_NO_OPTION, JOptionPane.QUESTION_MESSAGE) != JOptionPane.YES_OPTION) {
            return;
        }
        System.exit(0);
    }

    public CanvasMesh getCanvas() {
        return canvas;
    }

    public CanvasContainer getCanvasContainer() {
        return canvasContainer;
    }

    public static class RenderImageSettings extends Settings {
        public static final String RENDER = "Render";
        @BooleanType(name = "Write Rendered Frame", description = "Whether to write rendered frames to disk or not", category = RENDER)
        public boolean writeRenderedFrame = true;

        @DirectoryType(name = "Rendered Frame Dir", description = "Output directory to write rendered frames", default_ = "/", category = RENDER)
        public File renderedFrameDir = new File("C:/Users/Jasleen/Desktop/");

        @EnumType(name = "Rendered Frame Format", allowed = { "jpg", "png" }, category = RENDER)
        public String renderedFrameFormat = "png";

        @StringType(name = "Rendered Frame Name", category = RENDER)
        public String renderedFrameName = "frame";

        @Override
        public RenderImageSettings clone() {
            RenderImageSettings clone = new RenderImageSettings();
            clone.copyFrom(this);
            return clone;
        }

        @Override
        public void copyFrom(Settings copy) {
            RenderImageSettings s = (RenderImageSettings) copy;
            writeRenderedFrame = s.writeRenderedFrame;
            renderedFrameDir = new File(s.renderedFrameDir.getAbsolutePath());
            renderedFrameFormat = s.renderedFrameFormat;
            renderedFrameName = s.renderedFrameName;
        }

        @Override
        public String[] getCategories() {
            return new String[] { RENDER };
        }
    }

    public static class MeshEditorSettings extends SettingsContainer {
        public static final String GENERAL = "General";

        @IntegerType(name = "Undo Steps", description = "Select the number of Undo steps to preserve while editing", min = 0, max = 50, category = GENERAL)
        public int undoSteps = 10;

        public MeshEditorSettings(Settings... settings) {
            super(settings);
        }

        @Override
        public Settings clone() {
            MeshEditorSettings clone = new MeshEditorSettings(containedClone());
            clone.undoSteps = undoSteps;
            return clone;
        }

        @Override
        public void copyFrom(Settings copy) {
            MeshEditorSettings s = (MeshEditorSettings) copy;
            undoSteps = s.undoSteps;
            containedCopyFrom(s);
        }

        @Override
        public String[] getCategories() {
            List<String> list = new LinkedList<String>();
            list.add(GENERAL);
            list.addAll(Arrays.asList(containedCategories()));
            return list.toArray(new String[0]);
        }
    }

    public static void main(String[] args) throws Exception {
        // AlloyLookAndFeel.setProperty("alloy.licenseCode",
        // "2011/06/29#asidjoaisdjoasidjid@mailinator.com#lj8xv#1a193l");
        // AlloyLookAndFeel.setProperty("alloy.isToolbarEffectsEnabled",
        // "false");
        // try {
        // AlloyLookAndFeel alloyLnF = new AlloyLookAndFeel();
        // alloyLnF.setTheme(new GlassTheme(), true);
        // UIManager.setLookAndFeel(alloyLnF);
        // } catch (UnsupportedLookAndFeelException ex) {
        UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
        // }
        JFrame f = new JFrame("LiveCanvas - Mesh Editor");
        final MeshEditor meshEditor = new MeshEditor();
        f.setJMenuBar(meshEditor.getMenuBar());
        f.getContentPane().add(meshEditor);
        f.getContentPane().add(meshEditor.getToolBar(), BorderLayout.NORTH);
        f.pack();
        f.setLocationRelativeTo(null);
        f.addWindowListener(new WindowAdapter() {
            @Override
            public void windowClosing(WindowEvent e) {
                System.exit(0);
                // meshEditor.exit();
            }
        });
        f.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
        f.setVisible(true);
        // meshEditor.open(new File("C:/Users/Jasleen/Desktop/prez.mesh"));
    }
}