Project: ceres
/*
 * Copyright (C) 2010 Brockmann Consult GmbH ([email protected]
 * 
 * This program is free software; you can redistribute it and/or modify it 
 * under the terms of the GNU General Public License as published by the Free 
 * Software Foundation; either version 3 of the License, or (at your option) 
 * any later version. 
 * This program is distributed in the hope that it will be useful, but WITHOUT 
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 
 * more details. 
 * 
 * You should have received a copy of the GNU General Public License along 
 * with this program; if not, see http://www.gnu.org/licenses/ 
 */
 
package com.bc.ceres.swing.demo; 
 
import com.bc.ceres.glayer.swing.AdjustableViewScrollPane; 
import com.bc.ceres.swing.actions.CopyAction; 
import com.bc.ceres.swing.actions.CutAction; 
import com.bc.ceres.swing.actions.DeleteAction; 
import com.bc.ceres.swing.actions.PasteAction; 
import com.bc.ceres.swing.actions.RedoAction; 
import com.bc.ceres.swing.actions.SelectAllAction; 
import com.bc.ceres.swing.actions.UndoAction; 
import com.bc.ceres.swing.figure.AbstractInteractorListener; 
import com.bc.ceres.swing.figure.FigureCollection; 
import com.bc.ceres.swing.figure.FigureFactory; 
import com.bc.ceres.swing.figure.Interactor; 
import com.bc.ceres.swing.figure.interactions.InsertEllipseFigureInteractor; 
import com.bc.ceres.swing.figure.interactions.InsertLineFigureInteractor; 
import com.bc.ceres.swing.figure.interactions.InsertPolygonFigureInteractor; 
import com.bc.ceres.swing.figure.interactions.InsertPolylineFigureInteractor; 
import com.bc.ceres.swing.figure.interactions.InsertRectangleFigureInteractor; 
import com.bc.ceres.swing.figure.interactions.PanInteractor; 
import com.bc.ceres.swing.figure.interactions.SelectionInteractor; 
import com.bc.ceres.swing.figure.interactions.ZoomInteractor; 
import com.bc.ceres.swing.figure.support.DefaultFigureCollection; 
import com.bc.ceres.swing.figure.support.DefaultFigureStyle; 
import com.bc.ceres.swing.figure.support.FigureEditorPanel; 
import com.bc.ceres.swing.selection.SelectionChangeEvent; 
import com.bc.ceres.swing.selection.SelectionChangeListener; 
import com.bc.ceres.swing.selection.support.DefaultSelectionManager; 
import com.bc.ceres.swing.undo.support.DefaultUndoContext; 
 
import javax.swing.AbstractAction; 
import javax.swing.AbstractButton; 
import javax.swing.Action; 
import javax.swing.ButtonGroup; 
import javax.swing.JComponent; 
import javax.swing.JFileChooser; 
import javax.swing.JFrame; 
import javax.swing.JMenu; 
import javax.swing.JMenuBar; 
import javax.swing.JOptionPane; 
import javax.swing.JToggleButton; 
import javax.swing.JToolBar; 
import javax.swing.SwingUtilities; 
import javax.swing.UIManager; 
import javax.swing.event.UndoableEditEvent; 
import javax.swing.event.UndoableEditListener; 
import java.awt.BasicStroke; 
import java.awt.BorderLayout; 
import java.awt.Color; 
import java.awt.Dimension; 
import java.awt.Rectangle; 
import java.awt.datatransfer.FlavorEvent; 
import java.awt.datatransfer.FlavorListener; 
import java.awt.event.ActionEvent; 
import java.awt.event.ActionListener; 
import java.awt.geom.Area; 
import java.awt.geom.Ellipse2D; 
import java.awt.geom.Path2D; 
import java.awt.geom.Point2D; 
import java.io.File; 
import java.io.IOException; 
import java.text.MessageFormat; 
import java.util.Locale; 
import java.util.prefs.Preferences; 
 
public abstract class FigureEditorApp { 
    private static final Interactor SELECTION_INTERACTOR = new SelectionInteractor(); 
    private static final Interactor ZOOM_INTERACTOR = new ZoomInteractor(); 
    private static final Interactor PAN_INTERACTOR = new PanInteractor(); 
    private static final Interactor NEW_LINE_INTERACTOR = new InsertLineFigureInteractor(); 
    private static final Interactor NEW_RECT_INTERACTOR = new InsertRectangleFigureInteractor(); 
    private static final Interactor NEW_ELLI_INTERACTOR = new InsertEllipseFigureInteractor(); 
    private static final Interactor NEW_POLYLINE_INTERACTOR = new InsertPolylineFigureInteractor(); 
    private static final Interactor NEW_POLYGON_INTERACTOR = new InsertPolygonFigureInteractor(); 
 
    private JFrame frame; 
 
    private UndoAction undoAction; 
    private RedoAction redoAction; 
    private DeleteAction deleteAction; 
    private SelectAllAction selectAllAction; 
    private CutAction cutAction; 
    private CopyAction copyAction; 
    private PasteAction pasteAction; 
    private FigureEditorPanel figureEditorPanel; 
 
    static { 
        Locale.setDefault(Locale.ENGLISH); 
        try { 
            UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 
        } catch (Exception ignored) { 
            // ok 
        } 
    } 
 
    protected FigureEditorApp() { 
    } 
 
    protected abstract FigureFactory getFigureFactory(); 
 
    protected abstract void loadFigureCollection(File file, FigureCollection figureCollection) throws IOException; 
 
    protected abstract void storeFigureCollection(FigureCollection figureCollection, File file) throws IOException; 
 
    private void init() { 
 
        DefaultSelectionManager selectionManager = new DefaultSelectionManager(); 
        DefaultUndoContext undoContext = new DefaultUndoContext(this); 
 
        figureEditorPanel = new FigureEditorPanel(undoContext, new DefaultFigureCollection(), getFigureFactory()); 
        selectionManager.setSelectionContext(figureEditorPanel.getFigureEditor().getSelectionContext()); 
 
        undoAction = new UndoAction(undoContext) { 
            @Override 
            public void execute() { 
                super.execute(); 
                redoAction.updateState(); 
            } 
        }; 
        redoAction = new RedoAction(undoContext) { 
            @Override 
            public void execute() { 
                super.execute(); 
                undoAction.updateState(); 
            } 
        }; 
        cutAction = new CutAction(selectionManager); 
        copyAction = new CopyAction(selectionManager); 
        pasteAction = new PasteAction(selectionManager); 
        selectAllAction = new SelectAllAction(selectionManager); 
        deleteAction = new DeleteAction(selectionManager); 
 
        AbstractButton selectButton = createInteractorButton(figureEditorPanel, "S", SELECTION_INTERACTOR); 
        AbstractButton zoomButton = createInteractorButton(figureEditorPanel, "Z", ZOOM_INTERACTOR); 
        AbstractButton panButton = createInteractorButton(figureEditorPanel, "P", PAN_INTERACTOR); 
        AbstractButton newLineButton = createInteractorButton(figureEditorPanel, "L", NEW_LINE_INTERACTOR); 
        AbstractButton newRectButton = createInteractorButton(figureEditorPanel, "R", NEW_RECT_INTERACTOR); 
        AbstractButton newElliButton = createInteractorButton(figureEditorPanel, "E", NEW_ELLI_INTERACTOR); 
        AbstractButton newPLButton = createInteractorButton(figureEditorPanel, "PL", NEW_POLYLINE_INTERACTOR); 
        AbstractButton newPGButton = createInteractorButton(figureEditorPanel, "PG", NEW_POLYGON_INTERACTOR); 
 
        JToolBar toolBar = new JToolBar(); 
        toolBar.add(selectButton); 
        toolBar.add(zoomButton); 
        toolBar.add(panButton); 
        toolBar.add(newLineButton); 
        toolBar.add(newRectButton); 
        toolBar.add(newElliButton); 
        toolBar.add(newPLButton); 
        toolBar.add(newPGButton); 
 
        ButtonGroup group = new ButtonGroup(); 
        group.add(selectButton); 
        group.add(zoomButton); 
        group.add(panButton); 
        group.add(newLineButton); 
        group.add(newRectButton); 
        group.add(newElliButton); 
        group.add(newPLButton); 
        group.add(newPGButton); 
 
        figureEditorPanel.getFigureEditor().setInteractor(SELECTION_INTERACTOR); 
        figureEditorPanel.setPreferredSize(new Dimension(10241024)); 
 
        FigureCollection drawing = figureEditorPanel.getFigureEditor().getFigureCollection(); 
 
        FigureFactory figureFactory = figureEditorPanel.getFigureEditor().getFigureFactory(); 
        drawing.addFigure(figureFactory.createPolygonFigure(new Rectangle(2030200100), DefaultFigureStyle.createPolygonStyle(Color.BLUE, Color.GREEN))); 
        drawing.addFigure(figureFactory.createPolygonFigure(new Rectangle(9010100200), DefaultFigureStyle.createPolygonStyle(Color.MAGENTA, Color.ORANGE))); 
        Path2D linePath = rectPath(true1106070140); 
        drawing.addFigure(figureFactory.createLineFigure(linePath, DefaultFigureStyle.createLineStyle(Color.GRAY))); 
 
        linePath = new Path2D.Double(); 
        linePath.moveTo(11060); 
        linePath.lineTo(110 + 7060); 
        linePath.lineTo(110 + 7060 + 140); 
        drawing.addFigure(figureFactory.createLineFigure(linePath, DefaultFigureStyle.createLineStyle(Color.BLACK))); 
 
        linePath = new Path2D.Double(); 
        linePath.moveTo(200100); 
        linePath.lineTo(300200); 
        drawing.addFigure(figureFactory.createLineFigure(linePath, DefaultFigureStyle.createLineStyle(Color.MAGENTA, new BasicStroke(5.0f)))); 
 
        drawing.addFigure(figureFactory.createPolygonFigure(new Ellipse2D.Double(501008080), DefaultFigureStyle.createPolygonStyle(Color.YELLOW, Color.RED))); 
        drawing.addFigure(figureFactory.createPolygonFigure(new Ellipse2D.Double(220120150300), DefaultFigureStyle.createPolygonStyle(Color.GREEN, Color.BLUE))); 
 
        Area area = new Area(new Rectangle(00100100)); 
        area.subtract(new Area(new Rectangle(25255050))); 
        area.add(new Area(new Rectangle(75755050))); 
        area.subtract(new Area(new Rectangle(87872525))); 
        area.subtract(new Area(new Rectangle(-26, -265050))); 
//        drawing.addFigure(figureFactory.createPolygonalFigure(area, DefaultFigureStyle.createShapeStyle(Color.RED, Color.ORANGE))); 
 
        Path2D path = new Path2D.Double(Path2D.WIND_EVEN_ODD); 
        path.append(rectPath(true00100100), false); 
        path.append(rectPath(false12122525), false); 
        path.append(rectPath(false65652525), false); 
        DefaultFigureStyle shapeStyle = DefaultFigureStyle.createPolygonStyle(new Color(00255127), Color.ORANGE); 
        drawing.addFigure(figureFactory.createPolygonFigure(path, shapeStyle)); 
 
        for (int i = 0; i < 50; i++) { 
            DefaultFigureStyle pointStyle = new DefaultFigureStyle(); 
            pointStyle.setStrokeColor(new Color(0064)); 
            pointStyle.setStrokeOpacity(0.9); 
            pointStyle.setStrokeWidth(1.0); 
 
            int type = i % 4
            if (type == 0) { 
                pointStyle.setSymbolName("pin"); 
                pointStyle.setFillColor(new Color(128128255)); 
                pointStyle.setFillOpacity(0.7); 
            } else if (type == 1) { 
                pointStyle.setSymbolName("circle"); 
                pointStyle.setFillColor(new Color(1281280)); 
                pointStyle.setFillOpacity(0.7); 
            } else if (type == 2) { 
                pointStyle.setSymbolName("star"); 
            } else { 
                pointStyle.setSymbolImagePath("/com/bc/ceres/swing/update/icons/list-add.png"); 
                pointStyle.setSymbolRefX(8.0); 
                pointStyle.setSymbolRefY(8.0); 
            } 
            drawing.addFigure(figureFactory.createPointFigure(new Point2D.Double(i * 10, i * 10), pointStyle)); 
        } 
 
        /*
        Area a2 = new Area(); 
        a2.add(new Area(new Rectangle(0, 0, 100, 100))); 
        a2.subtract(new Area(new Rectangle(12, 12, 25, 25))); 
        a2.subtract(new Area(new Rectangle(65, 65, 25, 25))); 
        a2.add(new Area(new Rectangle(200, 200, 100, 100))); 
        a2.subtract(new Area(new Rectangle(200 + 12, 200 + 12, 25, 25))); 
        a2.subtract(new Area(new Rectangle(200 + 65, 200 + 65, 25, 25))); 
        drawing.addFigure(figureFactory.createPolygonalFigure(a2, DefaultFigureStyle.createShapeStyle(new Color(255, 255, 0, 127), Color.ORANGE))); 
        */
 
 
        JMenuBar menuBar = new JMenuBar(); 
        menuBar.add(createFileMenu()); 
        menuBar.add(createEditMenu()); 
 
        JComponent contentPane = new AdjustableViewScrollPane(figureEditorPanel); 
 
        frame = new JFrame(getClass().getSimpleName()); 
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 
        frame.setJMenuBar(menuBar); 
        frame.getContentPane().add(toolBar, BorderLayout.NORTH); 
        frame.getContentPane().add(contentPane, BorderLayout.CENTER); 
        frame.setSize(400400); 
 
        Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { 
            @Override 
            public void uncaughtException(Thread t, Throwable e) { 
                e.printStackTrace(); 
                String message = MessageFormat.format("" + 
                                                              "An internal error occurred!\n" + 
                                                              "Type: {0}\n" + 
                                                              "Message: {1}", e.getClass(), e.getMessage()); 
                JOptionPane.showMessageDialog(frame, message, 
                                              "Internal Error"
                                              JOptionPane.ERROR_MESSAGE); 
                System.exit(1); 
            } 
        }); 
 
        figureEditorPanel.getFigureEditor().getSelectionContext().addSelectionChangeListener(new SelectionChangeListener() { 
            @Override 
            public void selectionChanged(SelectionChangeEvent event) { 
                System.out.println("selection changed: " + event.getSelection()); 
            } 
 
            @Override 
            public void selectionContextChanged(SelectionChangeEvent event) { 
                System.out.println("selection context changed: " + event.getSelection()); 
            } 
        }); 
 
        undoContext.addUndoableEditListener(new UndoableEditListener() { 
            @Override 
            public void undoableEditHappened(UndoableEditEvent event) { 
                System.out.println("edit happened: " + event.getEdit()); 
            } 
        }); 
 
        selectionManager.getClipboard().addFlavorListener(new FlavorListener() { 
            @Override 
            public void flavorsChanged(FlavorEvent event) { 
                System.out.println("flavors changed: " + event); 
            } 
        }); 
    } 
 
 
    private static Path2D rectPath(boolean clockwise, int x, int y, int w, int h) { 
        Path2D.Double linePath = new Path2D.Double(); 
        linePath.moveTo(x, y); 
        if (clockwise) { 
            linePath.lineTo(x, y + h); 
            linePath.lineTo(x + w, y + h); 
            linePath.lineTo(x + w, y); 
        } else { 
            linePath.lineTo(x + w, y); 
            linePath.lineTo(x + w, y + h); 
            linePath.lineTo(x, y + h); 
        } 
        linePath.lineTo(x, y); 
        linePath.closePath(); 
        return linePath; 
    } 
 
    public static void run(FigureEditorApp drawingApp) { 
        drawingApp.init(); 
        drawingApp.run(); 
    } 
 
    public JFrame getFrame() { 
        return frame; 
    } 
 
    private void run() { 
        SwingUtilities.invokeLater(new Runnable() { 
            @Override 
            public void run() { 
                frame.setVisible(true); 
            } 
        }); 
    } 
 
    private JMenu createFileMenu() { 
        JMenu menu = new JMenu("File"); 
        menu.add(new OpenAction()); 
        menu.add(new SaveAsAction()); 
        menu.addSeparator(); 
        menu.add(new ExitAction()); 
        return menu; 
    } 
 
    private JMenu createEditMenu() { 
        JMenu menu = new JMenu("Edit"); 
        menu.add(undoAction); 
        menu.add(redoAction); 
        menu.addSeparator(); 
        menu.add(cutAction); 
        menu.add(copyAction); 
        menu.add(pasteAction); 
        menu.addSeparator(); 
        menu.add(selectAllAction); 
        menu.addSeparator(); 
        menu.add(deleteAction); 
        return menu; 
    } 
 
    private static AbstractButton createInteractorButton(final FigureEditorPanel figureEditorPanel, String name, final Interactor interactor) { 
        final AbstractButton selectButton = new JToggleButton(name); 
        selectButton.setSelected(false); 
        selectButton.addActionListener(new ActionListener() { 
            @Override 
            public void actionPerformed(ActionEvent e) { 
                boolean b = selectButton.isSelected(); 
                System.out.println("b = " + b); 
                if (b) { 
                    figureEditorPanel.getFigureEditor().setInteractor(interactor); 
                } 
            } 
        }); 
        interactor.addListener(new AbstractInteractorListener() { 
            @Override 
            public void interactorActivated(Interactor interactor) { 
                selectButton.setSelected(true); 
            } 
 
            @Override 
            public void interactorDeactivated(Interactor interactor) { 
                selectButton.setSelected(false); 
            } 
        }); 
        return selectButton; 
    } 
 
    private class OpenAction extends AbstractAction { 
        private OpenAction() { 
            putValue(Action.NAME, "Open..."); 
            putValue(Action.ACTION_COMMAND_KEY, getClass().getName()); 
        } 
 
        @Override 
        public void actionPerformed(ActionEvent event) { 
            File lastDir = new File(Preferences.userNodeForPackage(FigureEditorApp.class).get("lastDir"".")); 
            JFileChooser chooser = new JFileChooser(lastDir); 
            chooser.setAcceptAllFileFilterUsed(true); 
            int i = chooser.showOpenDialog(frame); 
            if (i == JFileChooser.APPROVE_OPTION && chooser.getSelectedFile() != null) { 
                Preferences.userNodeForPackage(FigureEditorApp.class).put("lastDir", chooser.getCurrentDirectory().getPath()); 
                figureEditorPanel.getFigureEditor().getFigureSelection().removeAllFigures(); 
                figureEditorPanel.getFigureEditor().getFigureCollection().removeAllFigures(); 
                try { 
                    loadFigureCollection(chooser.getSelectedFile(), figureEditorPanel.getFigureEditor().getFigureCollection()); 
                } catch (IOException e) { 
                    JOptionPane.showMessageDialog(getFrame(), "Error: " + e.getMessage()); 
                } 
            } 
        } 
    } 
 
    private class SaveAsAction extends AbstractAction { 
        private SaveAsAction() { 
            putValue(Action.NAME, "Save As..."); 
            putValue(Action.ACTION_COMMAND_KEY, getClass().getName()); 
        } 
 
        @Override 
        public void actionPerformed(ActionEvent event) { 
            File lastDir = new File(Preferences.userNodeForPackage(FigureEditorApp.class).get("lastDir"".")); 
            JFileChooser chooser = new JFileChooser(lastDir); 
            chooser.setAcceptAllFileFilterUsed(true); 
            int i = chooser.showSaveDialog(frame); 
            if (i == JFileChooser.APPROVE_OPTION && chooser.getSelectedFile() != null) { 
                Preferences.userNodeForPackage(FigureEditorApp.class).put("lastDir", chooser.getCurrentDirectory().getPath()); 
                try { 
                    storeFigureCollection(figureEditorPanel.getFigureEditor().getFigureCollection(), chooser.getSelectedFile()); 
                } catch (IOException e) { 
                    JOptionPane.showMessageDialog(getFrame(), "Error: " + e.getMessage()); 
                } 
            } 
        } 
    } 
 
    private class ExitAction extends AbstractAction { 
        private ExitAction() { 
            putValue(Action.NAME, "Exit"); 
            putValue(Action.ACTION_COMMAND_KEY, getClass().getName()); 
        } 
 
        @Override 
        public void actionPerformed(ActionEvent e) { 
            frame.dispose(); 
        } 
    } 
}