/*
* (c) Copyright 2010-2011 AgileBirds
*
* This file is part of OpenFlexo.
*
* OpenFlexo 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.
*
* OpenFlexo 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 OpenFlexo. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openflexo.fge.geomedit;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTree;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import org.openflexo.fge.GraphicalRepresentation;
import org.openflexo.fge.controller.DrawingController;
import org.openflexo.fge.cp.ControlArea;
import org.openflexo.fge.geomedit.edition.CreateBandFromLines;
import org.openflexo.fge.geomedit.edition.CreateCircleWithCenterAndPoint;
import org.openflexo.fge.geomedit.edition.CreateCubicCurveFromFourPoints;
import org.openflexo.fge.geomedit.edition.CreateCurveWithNPoints;
import org.openflexo.fge.geomedit.edition.CreateHalfBandWithLines;
import org.openflexo.fge.geomedit.edition.CreateHalfLineFromPoints;
import org.openflexo.fge.geomedit.edition.CreateHalfPlaneWithLineAndPoint;
import org.openflexo.fge.geomedit.edition.CreateHorizontalLineWithPoint;
import org.openflexo.fge.geomedit.edition.CreateIntersection;
import org.openflexo.fge.geomedit.edition.CreateLineFromPoints;
import org.openflexo.fge.geomedit.edition.CreateOrthogonalLineWithPoint;
import org.openflexo.fge.geomedit.edition.CreateParallelLineWithPoint;
import org.openflexo.fge.geomedit.edition.CreatePoint;
import org.openflexo.fge.geomedit.edition.CreatePointMiddleOfPoints;
import org.openflexo.fge.geomedit.edition.CreatePointSymetricOfPoint;
import org.openflexo.fge.geomedit.edition.CreatePolygonWithNPoints;
import org.openflexo.fge.geomedit.edition.CreatePolylinWithNPoints;
import org.openflexo.fge.geomedit.edition.CreateQuadCurveFromThreePoints;
import org.openflexo.fge.geomedit.edition.CreateRectPolylinWithStartAndEndArea;
import org.openflexo.fge.geomedit.edition.CreateRectangleFromPoints;
import org.openflexo.fge.geomedit.edition.CreateRotatedLineWithPoint;
import org.openflexo.fge.geomedit.edition.CreateRoundRectangleFromPoints;
import org.openflexo.fge.geomedit.edition.CreateSegmentFromPoints;
import org.openflexo.fge.geomedit.edition.CreateTangentLineWithCircleAndPoint;
import org.openflexo.fge.geomedit.edition.CreateVerticalLineWithPoint;
import org.openflexo.fge.geomedit.edition.Edition;
import org.openflexo.fge.geomedit.edition.EditionInput;
import org.openflexo.fge.view.DrawingView;
import org.openflexo.inspector.selection.EmptySelection;
import org.openflexo.inspector.selection.MultipleSelection;
import org.openflexo.inspector.selection.UniqueSelection;
import org.openflexo.logging.FlexoLogger;
public class GeomEditController extends DrawingController<GeometricDrawing> implements TreeSelectionListener {
private static final Logger logger = FlexoLogger.getLogger(GeomEditController.class.getPackage().getName());
private JPopupMenu contextualMenu;
private GraphicalRepresentation<?> contextualMenuInvoker;
private Point contextualMenuClickedPoint;
private GeometricObject copiedShape;
private Edition currentEdition = null;
private DefaultTreeModel treeModel;
private JTree tree;
private JPanel controlPanel;
private JPanel availableMethodsPanel;
private JLabel editionLabel;
private JButton cancelButton;
private JLabel positionLabel;
private String NO_EDITION_STRING = "No edition";
public GeomEditController(final GeometricDrawing aDrawing) {
super(aDrawing);
// !!!!! TAKE CARE !!!!!
// When i tried to activate painting cache,
// application was stuck in a call to fillRect() in SunGraphics2D while obtaining cache (print)
// I investigated and found that default width and height values (10000) were too much
// and freezed application in SUN's code (everything's ok with 1000 value for example)
// I was disgusted and stopped further investigation, but i strongly recommand not to use cache
// in the context of GeomEdit application
getPaintManager().disablePaintingCache();
treeModel = new DefaultTreeModel(aDrawing.getModel());
tree = new JTree(treeModel);
tree.setCellRenderer(new DefaultTreeCellRenderer() {
@Override
public Component getTreeCellRendererComponent(JTree tree, Object value, boolean selected, boolean expanded, boolean leaf,
int row, boolean hasFocus) {
JLabel returned = (JLabel) super.getTreeCellRendererComponent(tree, value, selected, expanded, leaf, row, hasFocus);
if (value instanceof GeometricSet) {
returned.setText(((GeometricSet) value).getTitle());
} else if (value instanceof GeometricObject) {
returned.setText(((GeometricObject) value).name);
}
return returned;
}
});
tree.addTreeSelectionListener(this);
controlPanel = new JPanel(new BorderLayout());
availableMethodsPanel = new JPanel(new FlowLayout(FlowLayout.LEFT, 5, 0));
controlPanel.add(availableMethodsPanel, BorderLayout.WEST);
cancelButton = new JButton("Cancel");
cancelButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
resetCurrentInput();
}
});
controlPanel.add(cancelButton, BorderLayout.EAST);
controlPanel.add(availableMethodsPanel, BorderLayout.WEST);
editionLabel = new JLabel(NO_EDITION_STRING);
positionLabel = new JLabel(" ");
resetCurrentInput();
contextualMenu = new JPopupMenu();
JMenu createPointItem = new JMenu("Create point");
JMenuItem createExplicitPoint = new JMenuItem("As explicit position");
createExplicitPoint.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreatePoint(GeomEditController.this));
}
});
createPointItem.add(createExplicitPoint);
JMenuItem createPointAsMiddleFromPointsItem = new JMenuItem("As middle of two other points");
createPointAsMiddleFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreatePointMiddleOfPoints(GeomEditController.this));
}
});
createPointItem.add(createPointAsMiddleFromPointsItem);
JMenuItem createPointSymetricOfPointItem = new JMenuItem("Symetric to an other point");
createPointSymetricOfPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreatePointSymetricOfPoint(GeomEditController.this));
}
});
createPointItem.add(createPointSymetricOfPointItem);
contextualMenu.add(createPointItem);
JMenu createLineItem = new JMenu("Create line");
JMenuItem createLineFromPointsItem = new JMenuItem("From points");
createLineFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateLineFromPoints(GeomEditController.this));
}
});
createLineItem.add(createLineFromPointsItem);
JMenuItem createHorizontalLineWithPointItem = new JMenuItem("Horizontal crossing point");
createHorizontalLineWithPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateHorizontalLineWithPoint(GeomEditController.this));
}
});
createLineItem.add(createHorizontalLineWithPointItem);
JMenuItem createVerticalLineWithPointItem = new JMenuItem("Vertical crossing point");
createVerticalLineWithPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateVerticalLineWithPoint(GeomEditController.this));
}
});
createLineItem.add(createVerticalLineWithPointItem);
JMenuItem createParallelLineWithPointItem = new JMenuItem("Parallel crossing point");
createParallelLineWithPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateParallelLineWithPoint(GeomEditController.this));
}
});
createLineItem.add(createParallelLineWithPointItem);
JMenuItem createOrthogonalLineWithPointItem = new JMenuItem("Orthogonal crossing point");
createOrthogonalLineWithPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateOrthogonalLineWithPoint(GeomEditController.this));
}
});
createLineItem.add(createOrthogonalLineWithPointItem);
JMenuItem createRotatedLineWithPointItem = new JMenuItem("Rotated line crossing point");
createRotatedLineWithPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateRotatedLineWithPoint(GeomEditController.this));
}
});
createLineItem.add(createRotatedLineWithPointItem);
JMenuItem createTangentLineWithCircleAndPointItem = new JMenuItem("Tangent to a circle and crossing point");
createTangentLineWithCircleAndPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateTangentLineWithCircleAndPoint(GeomEditController.this));
}
});
createLineItem.add(createTangentLineWithCircleAndPointItem);
contextualMenu.add(createLineItem);
JMenu createHalfLineItem = new JMenu("Create half-line");
JMenuItem createHalfLineFromPointsItem = new JMenuItem("From points");
createHalfLineFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateHalfLineFromPoints(GeomEditController.this));
}
});
createHalfLineItem.add(createHalfLineFromPointsItem);
contextualMenu.add(createHalfLineItem);
JMenu createSegmentItem = new JMenu("Create segment");
JMenuItem createSegmentFromPointsItem = new JMenuItem("From points");
createSegmentFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateSegmentFromPoints(GeomEditController.this));
}
});
createSegmentItem.add(createSegmentFromPointsItem);
contextualMenu.add(createSegmentItem);
JMenu createRectangleItem = new JMenu("Create rectangle");
JMenuItem createRectangleFromPointsItem = new JMenuItem("From points");
createRectangleFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateRectangleFromPoints(GeomEditController.this));
}
});
createRectangleItem.add(createRectangleFromPointsItem);
contextualMenu.add(createRectangleItem);
JMenu createRoundRectangleItem = new JMenu("Create rounded rectangle");
JMenuItem createRoundRectangleFromPointsItem = new JMenuItem("From points");
createRoundRectangleFromPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateRoundRectangleFromPoints(GeomEditController.this));
}
});
createRoundRectangleItem.add(createRoundRectangleFromPointsItem);
contextualMenu.add(createRoundRectangleItem);
JMenu createCircleItem = new JMenu("Create circle");
JMenuItem createCircleWithCenterAndPointItem = new JMenuItem("From center and point");
createCircleWithCenterAndPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateCircleWithCenterAndPoint(GeomEditController.this));
}
});
createCircleItem.add(createCircleWithCenterAndPointItem);
contextualMenu.add(createCircleItem);
JMenu createPolygonItem = new JMenu("Create polygon");
JMenuItem createPolygonWithNPointsItem = new JMenuItem("From points");
createPolygonWithNPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreatePolygonWithNPoints(GeomEditController.this));
}
});
createPolygonItem.add(createPolygonWithNPointsItem);
contextualMenu.add(createPolygonItem);
JMenu createPolylinItem = new JMenu("Create polylin");
JMenuItem createRectPolylinWithStartAndEndAreaItem = new JMenuItem("From start and end area");
createRectPolylinWithStartAndEndAreaItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateRectPolylinWithStartAndEndArea(GeomEditController.this));
}
});
createPolylinItem.add(createRectPolylinWithStartAndEndAreaItem);
JMenuItem createPolylinFromNPointsItem = new JMenuItem("From points");
createPolylinFromNPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreatePolylinWithNPoints(GeomEditController.this));
}
});
createPolylinItem.add(createPolylinFromNPointsItem);
contextualMenu.add(createPolylinItem);
JMenu createCurveItem = new JMenu("Create curve");
JMenuItem createQuadCurveWith3PointsItem = new JMenuItem("Quadradic curve with 3 points");
createQuadCurveWith3PointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateQuadCurveFromThreePoints(GeomEditController.this));
}
});
createCurveItem.add(createQuadCurveWith3PointsItem);
JMenuItem createCubicCurveWith4PointsItem = new JMenuItem("Cubic curve with 4 points");
createCubicCurveWith4PointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateCubicCurveFromFourPoints(GeomEditController.this));
}
});
createCurveItem.add(createCubicCurveWith4PointsItem);
JMenuItem createCurveWithNPointsItem = new JMenuItem("Complex curve crossing n points");
createCurveWithNPointsItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateCurveWithNPoints(GeomEditController.this));
}
});
createCurveItem.add(createCurveWithNPointsItem);
contextualMenu.add(createCurveItem);
JMenu createHalfPlaneItem = new JMenu("Create half-plane");
JMenuItem createHalfPlaneWithLineAndPointItem = new JMenuItem("From line and point");
createHalfPlaneWithLineAndPointItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateHalfPlaneWithLineAndPoint(GeomEditController.this));
}
});
createHalfPlaneItem.add(createHalfPlaneWithLineAndPointItem);
contextualMenu.add(createHalfPlaneItem);
JMenu createBandItem = new JMenu("Create band");
JMenuItem createBandFromLinesItem = new JMenuItem("From lines");
createBandFromLinesItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateBandFromLines(GeomEditController.this));
}
});
createBandItem.add(createBandFromLinesItem);
contextualMenu.add(createBandItem);
JMenu createHalfBandItem = new JMenu("Create half band");
JMenuItem createHalfBandFromLinesItem = new JMenuItem("From lines");
createHalfBandFromLinesItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateHalfBandWithLines(GeomEditController.this));
}
});
createHalfBandItem.add(createHalfBandFromLinesItem);
contextualMenu.add(createHalfBandItem);
contextualMenu.addSeparator();
JMenuItem createIntersectionItem = new JMenuItem("Create intersection");
createIntersectionItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
setCurrentEdition(new CreateIntersection(GeomEditController.this));
}
});
contextualMenu.add(createIntersectionItem);
contextualMenu.addSeparator();
JMenuItem copyItem = new JMenuItem("Copy");
copyItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
copy();
}
});
contextualMenu.add(copyItem);
JMenuItem pasteItem = new JMenuItem("Paste");
pasteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
paste();
}
});
contextualMenu.add(pasteItem);
JMenuItem cutItem = new JMenuItem("Cut");
cutItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
cut();
}
});
contextualMenu.add(cutItem);
}
public void showContextualMenu(GraphicalRepresentation gr, Point p) {
contextualMenuInvoker = gr;
contextualMenuClickedPoint = p;
contextualMenu.show(/*(Component)view*/getDrawingView(), p.x, p.y);
}
@Override
public void addToSelectedObjects(GraphicalRepresentation anObject) {
super.addToSelectedObjects(anObject);
if (getSelectedObjects().size() == 1) {
setChanged();
notifyObservers(new UniqueSelection(getSelectedObjects().get(0), null));
} else {
setChanged();
notifyObservers(new MultipleSelection());
}
tree.removeTreeSelectionListener(this);
Object[] path = { getDrawing().getModel(), anObject.getDrawable() };
tree.addSelectionPath(new TreePath(path));
tree.addTreeSelectionListener(this);
}
@Override
public void removeFromSelectedObjects(GraphicalRepresentation anObject) {
super.removeFromSelectedObjects(anObject);
if (getSelectedObjects().size() == 1) {
setChanged();
notifyObservers(new UniqueSelection(getSelectedObjects().get(0), null));
} else {
setChanged();
notifyObservers(new MultipleSelection());
}
tree.removeTreeSelectionListener(this);
Object[] path = { getDrawing().getModel(), anObject.getDrawable() };
tree.removeSelectionPath(new TreePath(path));
tree.addTreeSelectionListener(this);
}
@Override
public void clearSelection() {
super.clearSelection();
notifyObservers(new EmptySelection());
tree.removeTreeSelectionListener(this);
tree.clearSelection();
tree.addTreeSelectionListener(this);
}
@Override
public void selectDrawing() {
super.selectDrawing();
setChanged();
notifyObservers(new UniqueSelection(getDrawingGraphicalRepresentation(), null));
}
@Override
public DrawingView<GeometricDrawing> makeDrawingView(GeometricDrawing drawing) {
return new GeometricDrawingView(drawing, this);
}
@Override
public GeometricDrawingView getDrawingView() {
return (GeometricDrawingView) super.getDrawingView();
}
public void copy() {
/* if (contextualMenuInvoker instanceof GeometricObjectGraphicalRepresentation) {
copiedShape = (GeometricObject)(((GeometricObjectGraphicalRepresentation)getFocusedObjects().firstElement()).getDrawable().clone());
System.out.println("Copied: "+copiedShape);
}*/
}
public void paste() {
System.out.println("Paste in " + contextualMenuInvoker.getDrawable());
}
public void cut() {
}
public Edition getCurrentEdition() {
return currentEdition;
}
public void setCurrentEdition(Edition anEdition) {
currentEdition = anEdition;
if (anEdition != null) {
updateCurrentInput();
} else {
editionLabel.setText(NO_EDITION_STRING);
editionLabel.revalidate();
editionLabel.repaint();
resetCurrentInput();
}
}
private EditionInput currentInput;
public void updateCurrentInput() {
cancelButton.setEnabled(true);
// controlPanel.setVisible(true);
currentInput = currentEdition.inputs.get(currentEdition.currentStep);
currentInput.updateControlPanel(controlPanel, availableMethodsPanel);
editionLabel.setText(currentEdition.getLabel() + ", " + currentInput.getInputLabel() + ", " + currentInput.getActiveMethodLabel()
+ (currentInput.endOnRightClick() ? " (right-click to finish)" : ""));
editionLabel.revalidate();
editionLabel.repaint();
if (contextualMenu.isShowing()) {
contextualMenu.setVisible(false);
}
getDrawingView().enableEditionInputMethod(currentInput.getDerivedActiveMethod());
}
private void resetCurrentInput() {
if (currentInput != null) {
currentInput.resetControlPanel(controlPanel);
}
availableMethodsPanel.removeAll();
availableMethodsPanel.revalidate();
availableMethodsPanel.repaint();
// controlPanel.setVisible(false);
cancelButton.setEnabled(false);
editionLabel.setText(NO_EDITION_STRING);
editionLabel.revalidate();
editionLabel.repaint();
getDrawingView().disableEditionInputMethod();
currentEdition = null;
currentInput = null;
getDrawingView().revalidate();
getDrawingView().repaint();
}
public void currentInputGiven() {
if (currentEdition.next()) {
// Done
resetCurrentInput();
} else {
// Switch to next input
updateCurrentInput();
}
}
public JPanel getControlPanel() {
return controlPanel;
}
public JLabel getEditionLabel() {
return editionLabel;
}
public JLabel getPositionLabel() {
return positionLabel;
}
public JTree getTree() {
return tree;
}
public void notifiedObjectAdded() {
treeModel.reload();
}
public void notifiedObjectRemoved() {
treeModel.reload();
}
@Override
public void valueChanged(TreeSelectionEvent e) {
for (TreePath path : e.getPaths()) {
GraphicalRepresentation gr = null;
if (path.getLastPathComponent() instanceof GeometricSet) {
gr = ((GeometricSet) path.getLastPathComponent()).getGraphicalRepresentation();
}
if (path.getLastPathComponent() instanceof GeometricObject) {
gr = ((GeometricObject) path.getLastPathComponent()).getGraphicalRepresentation();
}
if (gr != null) {
if (e.isAddedPath(path)) {
addToSelectedObjects(gr);
} else {
removeFromSelectedObjects(gr);
}
}
}
}
/**
* Implements strategy to preferencially choose a control point or an other during focus retrieving strategy
*
* @param cp1
* @param cp2
* @return
*/
@Override
public ControlArea<?> preferredFocusedControlArea(ControlArea<?> cp1, ControlArea<?> cp2) {
if (cp1 instanceof DraggableControlPoint && !(cp2 instanceof DraggableControlPoint)) {
return cp1;
}
if (cp2 instanceof DraggableControlPoint && !(cp1 instanceof DraggableControlPoint)) {
return cp2;
}
if (cp1.isDraggable() && !cp2.isDraggable()) {
return cp1;
}
if (cp2.isDraggable() && !cp1.isDraggable()) {
return cp2;
}
return super.preferredFocusedControlArea(cp1, cp2);
}
}