/*
* Copyright (C) 2010 Brockmann Consult GmbH (info@brockmann-consult.de)
*
* 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.figure.support;
import com.bc.ceres.core.Assert;
import com.bc.ceres.core.ExtensibleObject;
import com.bc.ceres.grender.Rendering;
import com.bc.ceres.grender.Viewport;
import com.bc.ceres.grender.support.DefaultViewport;
import com.bc.ceres.swing.figure.Figure;
import com.bc.ceres.swing.figure.FigureChangeEvent;
import com.bc.ceres.swing.figure.FigureChangeListener;
import com.bc.ceres.swing.figure.FigureCollection;
import com.bc.ceres.swing.figure.FigureEditor;
import com.bc.ceres.swing.figure.FigureFactory;
import com.bc.ceres.swing.figure.FigureSelection;
import com.bc.ceres.swing.figure.FigureStyle;
import com.bc.ceres.swing.figure.InteractionDispatcher;
import com.bc.ceres.swing.figure.Interactor;
import com.bc.ceres.swing.figure.interactions.NullInteractor;
import com.bc.ceres.swing.selection.Selection;
import com.bc.ceres.swing.selection.SelectionChangeListener;
import com.bc.ceres.swing.selection.SelectionContext;
import com.bc.ceres.swing.selection.support.SelectionChangeSupport;
import com.bc.ceres.swing.undo.RestorableEdit;
import com.bc.ceres.swing.undo.UndoContext;
import com.bc.ceres.swing.undo.support.DefaultUndoContext;
import javax.swing.JComponent;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.datatransfer.Transferable;
import java.awt.datatransfer.UnsupportedFlavorException;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
public class DefaultFigureEditor extends ExtensibleObject implements FigureEditor {
private final UndoContext undoContext;
private Rectangle selectionRectangle;
private Interactor interactor;
private final JComponent editorComponent;
private FigureCollection figureCollection;
private final FigureSelection figureSelection;
private final SelectionChangeSupport selectionChangeSupport;
private final Viewport viewport;
private FigureFactory figureFactory;
private FigureStyle defaultLineStyle;
private FigureStyle defaultPolygonStyle;
public DefaultFigureEditor(JComponent editorComponent) {
this(editorComponent, new DefaultViewport(true), null, new DefaultFigureCollection(), new DefaultFigureFactory());
}
public DefaultFigureEditor(JComponent editorComponent,
Viewport viewport,
UndoContext undoContext,
FigureCollection figureCollection,
FigureFactory figureFactory) {
Assert.notNull(editorComponent, "editorComponent");
Assert.notNull(viewport, "viewport");
Assert.notNull(figureCollection, "figureCollection");
this.editorComponent = editorComponent;
this.editorComponent.setFocusable(true);
this.viewport = viewport;
this.interactor = NullInteractor.INSTANCE;
InteractionDispatcher interactionDispatcher = new InteractionDispatcher(this);
interactionDispatcher.registerListeners(this.editorComponent);
this.undoContext = undoContext != null ? undoContext : new DefaultUndoContext(this);
this.figureCollection = figureCollection;
this.figureSelection = new DefaultFigureSelection();
this.figureSelection.addChangeListener(new FigureSelectionMulticaster());
this.selectionChangeSupport = new SelectionChangeSupport(this);
RepaintHandler repaintHandler = new RepaintHandler();
this.figureCollection.addChangeListener(repaintHandler);
this.figureSelection.addChangeListener(repaintHandler);
this.figureFactory = figureFactory;
this.defaultLineStyle = DefaultFigureStyle.createLineStyle(new Color(255, 255, 255, 200),
new BasicStroke(1.5f));
this.defaultPolygonStyle = DefaultFigureStyle.createPolygonStyle(new Color(0, 0, 255, 200),
new Color(255, 255, 255, 200),
new BasicStroke(1.0f));
}
@Override
public JComponent getEditorComponent() {
return editorComponent;
}
@Override
public void insertFigures(boolean performInsert, Figure... figures) {
undoContext.postEdit(new FigureInsertEdit(this, performInsert, figures));
}
@Override
public void deleteFigures(boolean performDelete, Figure... figures) {
undoContext.postEdit(new FigureDeleteEdit(this, performDelete, figures));
}
@Override
public void changeFigure(Figure figure, Object figureMemento, String presentationName) {
undoContext.postEdit(new RestorableEdit(figure, figureMemento, presentationName));
}
@Override
public SelectionContext getSelectionContext() {
return this;
}
@Override
public Rectangle getSelectionRectangle() {
return selectionRectangle;
}
@Override
public void setSelectionRectangle(Rectangle newRect) {
Rectangle oldRect = selectionRectangle;
if (newRect == oldRect) {
return;
}
Rectangle2D repaintRect = null;
if (oldRect == null) {
selectionRectangle = newRect;
repaintRect = newRect;
} else if (newRect == null) {
selectionRectangle = newRect;
getEditorComponent().repaint(oldRect);
repaintRect = oldRect;
} else if (!oldRect.equals(newRect)) {
selectionRectangle = newRect;
repaintRect = oldRect.createUnion(newRect);
}
if (repaintRect != null) {
getEditorComponent().repaint((int) (repaintRect.getX() - 2),
(int) (repaintRect.getY() - 2),
(int) (repaintRect.getWidth() + 4),
(int) (repaintRect.getHeight()) + 4);
}
}
@Override
public FigureCollection getFigureCollection() {
return figureCollection;
}
public void setFigureCollection(FigureCollection figureCollection) {
if (this.figureCollection != figureCollection) {
figureSelection.removeAllFigures();
setSelectionRectangle(null);
this.figureCollection = figureCollection;
}
}
@Override
public FigureSelection getFigureSelection() {
return figureSelection;
}
@Override
public FigureFactory getFigureFactory() {
return figureFactory;
}
public void setFigureFactory(FigureFactory figureFactory) {
this.figureFactory = figureFactory;
}
@Override
public Selection getSelection() {
return figureSelection;
}
@Override
public void setSelection(Selection selection) {
// todo - implement (select all figures that are equal to the ones in selection)
}
@Override
public void addSelectionChangeListener(SelectionChangeListener listener) {
selectionChangeSupport.addSelectionChangeListener(listener);
}
@Override
public void removeSelectionChangeListener(SelectionChangeListener listener) {
selectionChangeSupport.removeSelectionChangeListener(listener);
}
@Override
public SelectionChangeListener[] getSelectionChangeListeners() {
return selectionChangeSupport.getSelectionChangeListeners();
}
@Override
public void insert(Transferable contents) throws IOException, UnsupportedFlavorException {
Figure[] figures = (Figure[]) contents.getTransferData(FigureTransferable.FIGURES_DATA_FLAVOR);
if (figures != null && figures.length > 0) {
insertFigures(true, figures);
}
}
@Override
public boolean canDeleteSelection() {
return !getFigureSelection().isEmpty();
}
@Override
public void deleteSelection() {
Figure[] figures = getFigureSelection().getFigures();
if (figures.length > 0) {
deleteFigures(true, figures);
}
}
@Override
public boolean canInsert(Transferable contents) {
return contents.isDataFlavorSupported(FigureTransferable.FIGURES_DATA_FLAVOR);
}
@Override
public void selectAll() {
figureSelection.removeAllFigures();
figureSelection.addFigures(getFigureCollection().getFigures());
figureSelection.setSelectionStage(figureSelection.getMaxSelectionStage());
}
@Override
public boolean canSelectAll() {
return getFigureCollection().getFigureCount() > 0;
}
@Override
public Interactor getInteractor() {
return interactor;
}
@Override
public void setInteractor(Interactor interactor) {
if (this.interactor != interactor) {
if (interactor != null) {
interactor.deactivate();
}
this.interactor = interactor;
if (this.interactor != null) {
this.interactor.activate();
}
getEditorComponent().setCursor(interactor.getCursor());
}
}
@Override
public Viewport getViewport() {
return viewport;
}
@Override
public FigureStyle getDefaultLineStyle() {
return defaultLineStyle;
}
public void setDefaultLineStyle(FigureStyle defaultLineStyle) {
Assert.notNull(defaultLineStyle, "defaultLineStyle");
this.defaultLineStyle = defaultLineStyle;
}
@Override
public FigureStyle getDefaultPolygonStyle() {
return defaultPolygonStyle;
}
public void setDefaultPolygonStyle(FigureStyle defaultPolygonStyle) {
Assert.notNull(defaultPolygonStyle, "defaultPolygonStyle");
this.defaultPolygonStyle = defaultPolygonStyle;
}
/**
* Calls
* <pre>
* drawFigureCollection(rendering);
* drawFigureSelection(rendering);
* drawSelectionRectangle(rendering);
* </pre>
*
* @param rendering The rendering.
*/
public void draw(Rendering rendering) {
drawFigureCollection(rendering);
drawFigureSelection(rendering);
drawSelectionRectangle(rendering);
}
public void drawFigureCollection(Rendering rendering) {
getFigureCollection().draw(rendering);
}
public void drawFigureSelection(Rendering rendering) {
getFigureSelection().draw(rendering);
}
public void drawSelectionRectangle(Rendering rendering) {
if (getSelectionRectangle() != null) {
Graphics2D graphics = rendering.getGraphics();
graphics.setPaint(StyleDefaults.SELECTION_RECT_FILL_PAINT);
graphics.fill(getSelectionRectangle());
graphics.setPaint(StyleDefaults.SELECTION_RECT_STROKE_PAINT);
graphics.draw(getSelectionRectangle());
}
}
private class RepaintHandler implements FigureChangeListener {
@Override
public void figureChanged(FigureChangeEvent event) {
getEditorComponent().repaint();
}
}
private class FigureSelectionMulticaster implements FigureChangeListener {
@Override
public void figureChanged(FigureChangeEvent event) {
if (event.getType() == FigureChangeEvent.FIGURES_ADDED
|| event.getType() == FigureChangeEvent.FIGURES_REMOVED) {
selectionChangeSupport.fireSelectionChange(DefaultFigureEditor.this, figureSelection);
}
}
}
}