/* * 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.grender.Rendering; import com.bc.ceres.swing.figure.*; import java.awt.*; import java.awt.geom.AffineTransform; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.util.*; import java.util.List; import java.util.Map.Entry; public class DefaultFigureCollection extends AbstractFigure implements FigureCollection { private List<Figure> figureList; private Set<Figure> figureSet; private Rectangle2D bounds; private ChangeDelegate changeDelegate; public DefaultFigureCollection() { this(NO_FIGURES); } public DefaultFigureCollection(Figure[] figures) { List<Figure> list = Arrays.asList(figures); init(list); } private synchronized void init(List<Figure> list) { setNormalStyle(new DefaultFigureStyle()); setSelectedStyle(new DefaultFigureStyle()); this.figureList = new ArrayList<Figure>(list); this.figureSet = new HashSet<Figure>(list); this.changeDelegate = new ChangeDelegate(); for (Figure figure : figureList) { figure.addChangeListener(changeDelegate); } addChangeListener(new BoundsUpdater()); } @Override public boolean isSelectable() { return true; } @Override public boolean isSelected() { if (getFigureCount() == 0) { return false; } for (Figure figure : getFigures()) { if (!figure.isSelected()) { return false; } } return true; } @Override public void setSelected(boolean selected) { if (isSelectable()) { for (Figure figure : getFigures()) { if (figure.isSelectable()) { figure.setSelected(selected); } } } } @Override public boolean isCollection() { return true; } @Override public Rank getRank() { return Rank.NOT_SPECIFIED; } @Override public synchronized void dispose() { removeFiguresImpl(); super.dispose(); } @Override public synchronized Object clone() { final DefaultFigureCollection figureCollection = (DefaultFigureCollection) super.clone(); figureCollection.init(figureList); return figureCollection; } @Override public int getMaxSelectionStage() { return 1; } @Override public synchronized Handle[] createHandles(int selectionStage) { if (getFigureCount() == 0) { return NO_HANDLES; } else if (getFigureCount() == 1) { return getFigure(0).createHandles(selectionStage); } else { return createScaleHandles(0.0); } } @Override public boolean isCloseTo(Point2D point, AffineTransform m2v) { return getBounds().contains(point); } @Override public synchronized boolean contains(Figure figure) { return figureSet.contains(figure); } @Override public synchronized int getFigureCount() { return figureList.size(); } @Override public synchronized int getFigureIndex(Figure figure) { return figureList.indexOf(figure); } @Override public synchronized Figure getFigure(int index) { return figureList.get(index); } @Override public synchronized Figure getFigure(Point2D p, AffineTransform m2v) { Figure selectedShape = null; for (Figure figure : getFigures()) { if (figure.isCloseTo(p, m2v)) { selectedShape = figure; } } return selectedShape; } @Override public synchronized Figure[] getFigures(Shape shape) { ArrayList<Figure> containedFigures = new ArrayList<Figure>(getFigureCount()); for (Figure figure : getFigures()) { if (shape.contains(figure.getBounds())) { containedFigures.add(figure); } } return containedFigures.toArray(new Figure[containedFigures.size()]); } @Override public synchronized Figure[] getFigures() { return figureList.toArray(new Figure[figureList.size()]); } @Override protected boolean addFigureImpl(Figure figure) { return addFigureImpl(getFigureCount(), figure); } @Override protected synchronized boolean addFigureImpl(int index, Figure figure) { figureSet.add(figure); figureList.add(index, figure); figure.addChangeListener(changeDelegate); return true; } @Override protected synchronized boolean removeFigureImpl(Figure figure) { boolean b = figureSet.remove(figure); if (b) { figureList.remove(figure); figure.removeChangeListener(changeDelegate); } return b; } @Override protected synchronized Figure[] removeFiguresImpl() { Figure[] allFigures = getFigures(); for (Figure figure : allFigures) { figure.removeChangeListener(changeDelegate); } figureSet.clear(); figureList.clear(); return allFigures; } @Override public synchronized Rectangle2D getBounds() { if (bounds == null) { bounds = computeBounds(); } return bounds; } @Override public synchronized void move(double dx, double dy) { if (getFigureCount() > 0) { for (Figure figure : getFigures()) { figure.move(dx, dy); } fireFigureChanged(); } } @Override public synchronized void scale(Point2D refPoint, double sx, double sy) { if (getFigureCount() > 0) { for (Figure figure : getFigures()) { figure.scale(refPoint, sx, sy); } fireFigureChanged(); } } @Override public synchronized void rotate(Point2D point, double theta) { if (getFigureCount() > 0) { for (Figure figure : getFigures()) { figure.rotate(point, theta); } fireFigureChanged(); } } @Override public synchronized void draw(Rendering rendering) { for (Figure figure : getFigures()) { figure.draw(rendering); } } @Override public synchronized Object createMemento() { if (getFigureCount() == 0) { return null; } final Figure[] figures = getFigures(); Map<Figure, Object> mementoMap = new HashMap<Figure, Object>(figures.length); for (Figure figure : figures) { mementoMap.put(figure, figure.createMemento()); } return mementoMap; } @Override public synchronized void setMemento(Object memento) { if (memento != null) { Map<Figure, Object> mementoMap = (Map<Figure, Object>) memento; for (Entry<Figure, Object> entry : mementoMap.entrySet()) { Figure figure = entry.getKey(); figure.setMemento(entry.getValue()); } fireFigureChanged(); } } protected synchronized Rectangle2D computeBounds() { final Rectangle2D bounds = new Rectangle2D.Double(); final Figure[] figures = getFigures(); if (figures.length > 0) { bounds.setRect(figures[0].getBounds()); for (int i = 1; i < figures.length; i++) { Figure figure = figures[i]; bounds.add(figure.getBounds()); } } return bounds; } private synchronized void nullBounds() { bounds = null; } private class BoundsUpdater implements FigureChangeListener { @Override public void figureChanged(FigureChangeEvent e) { nullBounds(); } } private class ChangeDelegate implements FigureChangeListener { @Override public void figureChanged(FigureChangeEvent e) { fireFigureChanged(e); } } }