package com.indago.iddea.view.display;
import edu.umd.cs.findbugs.annotations.Nullable;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Insets;
import java.awt.Paint;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.Shape;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.NoninvertibleTransformException;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
import javax.swing.undo.*;
import org.jhotdraw.util.*;
import org.jhotdraw.draw.AttributeKey;
import org.jhotdraw.draw.Constrainer;
import org.jhotdraw.draw.DefaultDrawingViewTransferHandler;
import org.jhotdraw.draw.Drawing;
import org.jhotdraw.draw.DrawingEditor;
import org.jhotdraw.draw.DrawingView;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.GridConstrainer;
import org.jhotdraw.draw.event.FigureSelectionEvent;
import org.jhotdraw.draw.event.FigureSelectionListener;
import org.jhotdraw.draw.handle.Handle;
import org.jhotdraw.draw.event.HandleListener;
import org.jhotdraw.draw.event.HandleEvent;
import org.jhotdraw.draw.event.FigureListener;
import org.jhotdraw.draw.event.FigureAdapter;
import org.jhotdraw.draw.event.FigureEvent;
import org.jhotdraw.draw.event.CompositeFigureListener;
import org.jhotdraw.draw.event.CompositeFigureEvent;
import org.jhotdraw.gui.EditableComponent;
import static org.jhotdraw.draw.AttributeKeys.*;
/**
* InteractiveDrawingView provides modified drawing functions rather than jhotdraw's
* DefaultDrawingView.
*
* @version 0.1beta
* @since 8/12/13 5:08 PM
* @author HongKee Moon
*/
public abstract class InteractiveDrawingView extends JComponent implements DrawingView,
EditableComponent {
/**
* Set this to true to turn on debugging output on System.out.
*/
private final static boolean DEBUG = false;
@Nullable
protected Drawing drawing;
/**
* Holds the selected figures in an ordered put. The ordering reflects
* the sequence that was used to select the figures.
*/
private Set<Figure> selectedFigures = new LinkedHashSet<Figure>();
private LinkedList<Handle> selectionHandles = new LinkedList<Handle>();
private boolean isConstrainerVisible = false;
private Constrainer visibleConstrainer = new GridConstrainer(8, 8);
private Constrainer invisibleConstrainer = new GridConstrainer();
private Handle secondaryHandleOwner;
@Nullable private Handle activeHandle;
private LinkedList<Handle> secondaryHandles = new LinkedList<Handle>();
private boolean handlesAreValid = true;
@Nullable
private transient Dimension cachedPreferredSize;
private int detailLevel;
@Nullable
protected DrawingEditor editor;
protected JLabel emptyDrawingLabel;
protected BufferedImage backgroundTile;
private FigureListener handleInvalidator = new FigureAdapter() {
@Override
public void figureHandlesChanged(FigureEvent e) {
invalidateHandles();
}
};
@Nullable
private transient Rectangle2D.Double cachedDrawingArea;
public final static String DRAWING_DOUBLE_BUFFERED_PROPERTY = "drawingDoubleBuffered";
/** Whether the drawing is double buffered*/
private boolean isDrawingDoubleBuffered = true;
private boolean paintEnabled = true;
public final static boolean isWindows;
/**
* The {@link AffineTransform} stores the previous transform to restore in the next transformation.
*/
//private AffineTransform preTransform = new AffineTransform(0.7775, 0.0, 0.0, 0.7775, 0.0, 66.75);
protected AffineTransform preTransform = new AffineTransform();
static {
boolean b = false;
try {
if (System.getProperty("os.name").toLowerCase().startsWith("win")) {
b = true;
}
} catch (Throwable t) {
}
isWindows = b;
}
public void copyFrom(InteractiveDrawingView view)
{
this.setDrawing(view.getDrawing());
this.addToSelection(view.getSelectedFigures());
this.setSelectedFigures(view.getSelectedFigures());
this.setSelectionHandles(view.getSelectionHandles());
this.setSecondarySelectionHandles(view.getSecondaryHandles());
}
private void setSelectedFigures(Set<Figure> figures)
{
selectedFigures = new LinkedHashSet<Figure>();
for(Figure f: figures)
selectedFigures.add(f);
}
private void setSelectionHandles(List<Handle> handles)
{
selectionHandles = new LinkedList<Handle>();
for(Handle h : handles)
selectionHandles.add(h);
}
private void setSecondarySelectionHandles(List<Handle> handles)
{
secondaryHandles = new LinkedList<Handle>();
for(Handle h : handles)
secondaryHandles.add(h);
}
public Set<Figure> getAllFigures() {
Set<Figure> allFigures = new HashSet<Figure>();
for (Figure figure : drawing.getChildren()) {
if (figure.isSelectable()) {
allFigures.add(figure);
}
}
return allFigures;
}
@Override
public void repaintHandles() {
validateHandles();
Rectangle r = null;
for (Handle h : getSelectionHandles()) {
if (r == null) {
r = h.getDrawingArea();
} else {
r.add(h.getDrawingArea());
}
}
for (Handle h : getSecondaryHandles()) {
if (r == null) {
r = h.getDrawingArea();
} else {
r.add(h.getDrawingArea());
}
}
if (r != null) {
repaint(r);
}
}
@Override
public boolean isSelectionEmpty() {
return selectedFigures.isEmpty();
}
private class EventHandler implements FigureListener, CompositeFigureListener, HandleListener, FocusListener {
@Override
public void figureAdded(CompositeFigureEvent evt) {
if (drawing.getChildCount() == 1 && getEmptyDrawingMessage() != null) {
repaint();
} else {
repaintDrawingArea(evt.getInvalidatedArea());
}
invalidateDimension();
}
@Override
public void figureRemoved(CompositeFigureEvent evt) {
if (drawing.getChildCount() == 0 && getEmptyDrawingMessage() != null) {
repaint();
} else {
repaintDrawingArea(evt.getInvalidatedArea());
}
removeFromSelection(evt.getChildFigure());
invalidateDimension();
}
@Override
public void areaInvalidated(FigureEvent evt) {
repaintDrawingArea(evt.getInvalidatedArea());
invalidateDimension();
}
@Override
public void areaInvalidated(HandleEvent evt) {
repaint(evt.getInvalidatedArea());
invalidateDimension();
}
@Override
public void handleRequestSecondaryHandles(HandleEvent e) {
secondaryHandleOwner = e.getHandle();
secondaryHandles.clear();
secondaryHandles.addAll(secondaryHandleOwner.createSecondaryHandles());
for (Handle h : secondaryHandles) {
h.setView(InteractiveDrawingView.this);
h.addHandleListener(eventHandler);
}
repaint();
}
@Override
public void focusGained(FocusEvent e) {
// repaintHandles();
if (editor != null) {
editor.setActiveView(InteractiveDrawingView.this);
}
}
@Override
public void focusLost(FocusEvent e) {
// repaintHandles();
}
@Override
public void handleRequestRemove(HandleEvent e) {
selectionHandles.remove(e.getHandle());
e.getHandle().dispose();
invalidateHandles();
repaint(e.getInvalidatedArea());
}
@Override
public void attributeChanged(FigureEvent e) {
if (e.getSource() == drawing) {
AttributeKey a = e.getAttribute();
if (a.equals(CANVAS_HEIGHT) || a.equals(CANVAS_WIDTH)) {
validateViewTranslation();
repaint(); // must repaint everything
}
if (e.getInvalidatedArea() != null) {
repaintDrawingArea(e.getInvalidatedArea());
} else {
repaintDrawingArea(viewToDrawing(getCanvasViewBounds()));
}
} else {
if (e.getInvalidatedArea() != null) {
repaintDrawingArea(e.getInvalidatedArea());
}
}
}
@Override
public void figureHandlesChanged(FigureEvent e) {
}
@Override
public void figureChanged(FigureEvent e) {
repaintDrawingArea(e.getInvalidatedArea());
}
@Override
public void figureAdded(FigureEvent e) {
}
@Override
public void figureRemoved(FigureEvent e) {
}
@Override
public void figureRequestRemove(FigureEvent e) {
}
}
private EventHandler eventHandler;
/** Creates new instance. */
public InteractiveDrawingView() {
initComponents();
eventHandler = createEventHandler();
setToolTipText("dummy"); // Set a dummy tool tip text to turn tooltips on
setFocusable(true);
addFocusListener(eventHandler);
setTransferHandler(new DefaultDrawingViewTransferHandler());
setBackground(new Color(0xb0b0b0));
setOpaque(true);
}
protected EventHandler createEventHandler() {
return new EventHandler();
}
/** This method is called from within the constructor to
* initialize the form.<p>
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.<p>
* NOTE: To prevent undesired layout effects when using floating
* text fields, the DefaultDrawingView must not use a layout manager.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
setLayout(null);
}// </editor-fold>//GEN-END:initComponents
@Override
@Nullable
public Drawing getDrawing() {
return drawing;
}
@Override
public String getToolTipText(MouseEvent evt) {
if (getEditor() != null && getEditor().getTool() != null) {
return getEditor().getTool().getToolTipText(this, evt);
}
return null;
}
public void setEmptyDrawingMessage(String newValue) {
String oldValue = (emptyDrawingLabel == null) ? null : emptyDrawingLabel.getText();
if (newValue == null) {
emptyDrawingLabel = null;
} else {
emptyDrawingLabel = new JLabel(newValue);
emptyDrawingLabel.setHorizontalAlignment(JLabel.CENTER);
}
firePropertyChange("emptyDrawingMessage", oldValue, newValue);
repaint();
}
public String getEmptyDrawingMessage() {
return (emptyDrawingLabel == null) ? null : emptyDrawingLabel.getText();
}
public abstract void drawImage(Graphics2D g);
/**
* Paints the drawing view.
* Uses rendering hints for fast painting. Paints the canvasColor, the
* grid, the drawing, the handles and the current tool.
*/
@Override
public void paintComponent(Graphics gr) {
Graphics2D g = (Graphics2D) gr;
setViewRenderingHints(g);
drawImage(g);
drawCanvas(g);
drawConstrainer(g);
drawDrawing(g);
drawHandles(g);
drawTool(g);
}
/**
* Prints the drawing view.
* Uses high quality rendering hints for printing. Only prints the drawing.
* Doesn't print the canvasColor, the grid, the handles and the tool.
*/
@Override
public void printComponent(Graphics gr) {
Graphics2D g = (Graphics2D) gr;
// Set rendering hints for quality
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BICUBIC);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
drawDrawing(g);
}
protected void setViewRenderingHints(Graphics2D g) {
// Set rendering hints for speed
g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR);
g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED);
g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
/**
* Returns the bounds of the canvas on the drawing view.
*
* @return The current bounds of the canvas on the drawing view.
*/
protected Rectangle getCanvasViewBounds() {
// Position of the zero coordinate point on the view
// int x = -translation.x;
// int y = -translation.y;
int x = 1;
int y = 1;
int w = getWidth();
int h = getHeight();
if (getDrawing() != null) {
Double cw = getDrawing().get(CANVAS_WIDTH);
Double ch = getDrawing().get(CANVAS_HEIGHT);
if (cw != null && ch != null) {
Point lowerRight = drawingToView(
new Point2D.Double(cw, ch));
w = lowerRight.x - x;
h = lowerRight.y - y;
}
}
return new Rectangle(x, y, w, h);
}
/** Draws the canvas. If the {@code AttributeKeys.CANVAS_FILL_OPACITY} is
* not fully opaque, the canvas area is filled with the background paint
* before the {@code AttributeKeys.CANVAS_FILL_COLOR} is drawn.
*/
protected void drawCanvas(Graphics2D gr) {
if (drawing != null) {
Graphics2D g = (Graphics2D) gr.create();
AffineTransform tx = g.getTransform();
tx.concatenate(preTransform);
g.setTransform(preTransform);
drawing.setFontRenderContext(g.getFontRenderContext());
drawing.drawCanvas(g);
g.dispose();
}
}
protected void drawConstrainer(Graphics2D g) {
Shape clip = g.getClip();
Rectangle r = getCanvasViewBounds();
g.clipRect(r.x, r.y, r.width, r.height);
getConstrainer().draw(g, this);
g.setClip(clip);
}
protected void drawDrawing(Graphics2D gr) {
if (drawing != null) {
if (drawing.getChildCount() == 0 && emptyDrawingLabel != null) {
emptyDrawingLabel.setBounds(0, 0, getWidth(), getHeight());
emptyDrawingLabel.paint(gr);
} else {
Graphics2D g = (Graphics2D) gr.create();
AffineTransform tx = g.getTransform();
tx.concatenate(preTransform);
g.setTransform(tx);
drawing.setFontRenderContext(g.getFontRenderContext());
drawing.draw(g);
g.dispose();
}
}
}
protected void drawHandles(java.awt.Graphics2D g) {
if (editor != null && editor.getActiveView() == this) {
validateHandles();
for (Handle h : getSelectionHandles()) {
h.draw(g);
}
for (Handle h : getSecondaryHandles()) {
h.draw(g);
}
}
}
protected void drawTool(Graphics2D g) {
if (editor != null && editor.getActiveView() == this && editor.getTool() != null) {
editor.getTool().draw(g);
}
}
@Override
public void setDrawing(@Nullable Drawing newValue) {
Drawing oldValue = drawing;
if (this.drawing != null) {
this.drawing.removeCompositeFigureListener(eventHandler);
this.drawing.removeFigureListener(eventHandler);
clearSelection();
}
this.drawing = newValue;
if (this.drawing != null) {
this.drawing.addCompositeFigureListener(eventHandler);
this.drawing.addFigureListener(eventHandler);
}
firePropertyChange(DRAWING_PROPERTY, oldValue, newValue);
// Revalidate without flickering
revalidate();
validateViewTranslation();
paintEnabled = false;
javax.swing.Timer t = new javax.swing.Timer(10, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
repaint();
paintEnabled = true;
}
});
t.setRepeats(false);
t.start();
}
@Override
public void paint(Graphics g) {
if (paintEnabled) {
super.paint(g);
}
}
protected void repaintDrawingArea(Rectangle2D.Double r) {
Rectangle vr = drawingToView(r);
repaint(vr);
}
@Override
public void invalidate() {
}
@Override
public void removeNotify() {
super.removeNotify();
}
/**
* Adds a figure to the current selection.
*/
@Override
public void addToSelection(Figure figure) {
if (DEBUG) {
System.out.println("DefaultDrawingView" + ".addToSelection(" + figure + ")");
}
Set<Figure> oldSelection = new HashSet<Figure>(selectedFigures);
if (selectedFigures.add(figure)) {
figure.addFigureListener(handleInvalidator);
Set<Figure> newSelection = new HashSet<Figure>(selectedFigures);
Rectangle invalidatedArea = null;
if (handlesAreValid && getEditor() != null) {
for (Handle h : figure.createHandles(detailLevel)) {
h.setView(this);
selectionHandles.add(h);
h.addHandleListener(eventHandler);
if (invalidatedArea == null) {
invalidatedArea = h.getDrawingArea();
} else {
invalidatedArea.add(h.getDrawingArea());
}
}
}
fireSelectionChanged(oldSelection, newSelection);
if (invalidatedArea != null) {
repaint(invalidatedArea);
}
}
}
/**
* Adds a collection of figures to the current selection.
*/
@Override
public void addToSelection(Collection<Figure> figures) {
Set<Figure> oldSelection = new HashSet<Figure>(selectedFigures);
Set<Figure> newSelection = new HashSet<Figure>(selectedFigures);
boolean selectionChanged = false;
Rectangle invalidatedArea = null;
for (Figure figure : figures) {
if (selectedFigures.add(figure)) {
selectionChanged = true;
newSelection.add(figure);
figure.addFigureListener(handleInvalidator);
if (handlesAreValid && getEditor() != null) {
for (Handle h : figure.createHandles(detailLevel)) {
h.setView(this);
selectionHandles.add(h);
h.addHandleListener(eventHandler);
if (invalidatedArea == null) {
invalidatedArea = h.getDrawingArea();
} else {
invalidatedArea.add(h.getDrawingArea());
}
}
}
}
}
if (selectionChanged) {
fireSelectionChanged(oldSelection, newSelection);
if (invalidatedArea != null) {
repaint(invalidatedArea);
}
}
}
/**
* Removes a figure from the selection.
*/
@Override
public void removeFromSelection(Figure figure) {
Set<Figure> oldSelection = new HashSet<Figure>(selectedFigures);
if (selectedFigures.remove(figure)) {
Set<Figure> newSelection = new HashSet<Figure>(selectedFigures);
invalidateHandles();
figure.removeFigureListener(handleInvalidator);
fireSelectionChanged(oldSelection, newSelection);
repaint();
}
}
/**
* If a figure isn't selected it is added to the selection.
* Otherwise it is removed from the selection.
*/
@Override
public void toggleSelection(Figure figure) {
if (selectedFigures.contains(figure)) {
removeFromSelection(figure);
} else {
addToSelection(figure);
}
}
@Override
public void setEnabled(boolean b) {
super.setEnabled(b);
setCursor(Cursor.getPredefinedCursor(b ? Cursor.DEFAULT_CURSOR : Cursor.WAIT_CURSOR));
}
/**
* Selects all selectable figures.
*/
@Override
public void selectAll() {
Set<Figure> oldSelection = new HashSet<Figure>(selectedFigures);
selectedFigures.clear();
for (Figure figure : drawing.getChildren()) {
if (figure.isSelectable()) {
selectedFigures.add(figure);
}
}
Set<Figure> newSelection = new HashSet<Figure>(selectedFigures);
invalidateHandles();
fireSelectionChanged(oldSelection, newSelection);
repaint();
}
/**
* Clears the current selection.
*/
@Override
public void clearSelection() {
if (getSelectionCount() > 0) {
Set<Figure> oldSelection = new HashSet<Figure>(selectedFigures);
selectedFigures.clear();
Set<Figure> newSelection = new HashSet<Figure>(selectedFigures);
invalidateHandles();
fireSelectionChanged(oldSelection, newSelection);
}
}
/**
* Test whether a given figure is selected.
*/
@Override
public boolean isFigureSelected(Figure checkFigure) {
return selectedFigures.contains(checkFigure);
}
/**
* Gets the current selection as a FigureSelection. A FigureSelection
* can be cut, copied, pasted.
*/
@Override
public Set<Figure> getSelectedFigures() {
return Collections.unmodifiableSet(selectedFigures);
}
/**
* Gets the number of selected figures.
*/
@Override
public int getSelectionCount() {
return selectedFigures.size();
}
/**
* Gets the currently active selection handles.
*/
protected java.util.List<Handle> getSelectionHandles() {
validateHandles();
return Collections.unmodifiableList(selectionHandles);
}
/**
* Gets the currently active secondary handles.
*/
protected java.util.List<Handle> getSecondaryHandles() {
validateHandles();
return Collections.unmodifiableList(secondaryHandles);
}
/**
* Invalidates the handles.
*/
protected void invalidateHandles() {
if (handlesAreValid) {
handlesAreValid = false;
Rectangle invalidatedArea = null;
for (Handle handle : selectionHandles) {
handle.removeHandleListener(eventHandler);
if (invalidatedArea == null) {
invalidatedArea = handle.getDrawingArea();
} else {
invalidatedArea.add(handle.getDrawingArea());
}
handle.dispose();
}
for (Handle handle : secondaryHandles) {
handle.removeHandleListener(eventHandler);
if (invalidatedArea == null) {
invalidatedArea = handle.getDrawingArea();
} else {
invalidatedArea.add(handle.getDrawingArea());
}
handle.dispose();
}
selectionHandles.clear();
secondaryHandles.clear();
setActiveHandle(null);
if (invalidatedArea != null) {
repaint(invalidatedArea);
}
}
}
/**
* Validates the handles.
*/
protected void validateHandles() {
// Validate handles only, if they are invalid, and if
// the DrawingView has a DrawingEditor.
if (!handlesAreValid && getEditor() != null) {
handlesAreValid = true;
selectionHandles.clear();
Rectangle invalidatedArea = null;
while (true) {
for (Figure figure : getSelectedFigures()) {
for (Handle handle : figure.createHandles(detailLevel)) {
handle.setView(this);
selectionHandles.add(handle);
handle.addHandleListener(eventHandler);
if (invalidatedArea == null) {
invalidatedArea = handle.getDrawingArea();
} else {
invalidatedArea.add(handle.getDrawingArea());
}
}
}
if (selectionHandles.size() == 0 && detailLevel != 0) {
// No handles are available at the desired detail level.
// Retry with detail level 0.
detailLevel = 0;
continue;
}
break;
}
if (invalidatedArea != null) {
repaint(invalidatedArea);
}
}
}
/**
* Finds a handle at a given coordinates.
* @return A handle, null if no handle is found.
*/
@Override
public Handle findHandle(
Point p) {
validateHandles();
for (Handle handle : new ReversedList<Handle>(getSecondaryHandles())) {
if (handle.contains(p)) {
return handle;
}
}
for (Handle handle : new ReversedList<Handle>(getSelectionHandles())) {
if (handle.contains(p)) {
return handle;
}
}
return null;
}
/**
* Gets compatible handles.
* @return A collection containing the handle and all compatible handles.
*/
@Override
public Collection<Handle> getCompatibleHandles(Handle master) {
validateHandles();
HashSet<Figure> owners = new HashSet<Figure>();
LinkedList<Handle> compatibleHandles = new LinkedList<Handle>();
owners.add(master.getOwner());
compatibleHandles.add(master);
for (Handle handle : getSelectionHandles()) {
if (!owners.contains(handle.getOwner()) && handle.isCombinableWith(master)) {
owners.add(handle.getOwner());
compatibleHandles.add(handle);
}
}
return compatibleHandles;
}
/**
* Finds a figure at a given coordinates.
* @return A figure, null if no figure is found.
*/
@Override
public Figure findFigure(
Point p) {
return getDrawing().findFigure(viewToDrawing(p));
}
@Override
public Collection<Figure> findFigures(Rectangle r) {
return getDrawing().findFigures(viewToDrawing(r));
}
@Override
public Collection<Figure> findFiguresWithin(Rectangle r) {
return getDrawing().findFiguresWithin(viewToDrawing(r));
}
@Override
public void addFigureSelectionListener(FigureSelectionListener fsl) {
listenerList.add(FigureSelectionListener.class, fsl);
}
@Override
public void removeFigureSelectionListener(FigureSelectionListener fsl) {
listenerList.remove(FigureSelectionListener.class, fsl);
}
/**
* Notify all listenerList that have registered interest for
* notification on this event type.
* Also notify listeners who listen for
* {@link EditableComponent#SELECTION_EMPTY_PROPERTY}.
*/
protected void fireSelectionChanged(
Set<Figure> oldValue,
Set<Figure> newValue) {
if (listenerList.getListenerCount() > 0) {
FigureSelectionEvent event = null;
// Notify all listeners that have registered interest for
// Guaranteed to return a non-null array
Object[] listeners = listenerList.getListenerList();
// Process the listeners last to first, notifying
// those that are interested in this event
for (int i = listeners.length - 2; i
>= 0; i -=
2) {
if (listeners[i] == FigureSelectionListener.class) {
// Lazily create the event:
if (event == null) {
event = new FigureSelectionEvent(this, oldValue, newValue);
}
((FigureSelectionListener) listeners[i + 1]).selectionChanged(event);
}
}
}
firePropertyChange(EditableComponent.SELECTION_EMPTY_PROPERTY, oldValue.isEmpty(), newValue.isEmpty());
}
protected void invalidateDimension() {
cachedPreferredSize = null;
cachedDrawingArea = null;
}
@Override
public Constrainer getConstrainer() {
return isConstrainerVisible() ? visibleConstrainer : invisibleConstrainer;
}
@Override
public Dimension getPreferredSize() {
if (cachedPreferredSize == null) {
Rectangle2D.Double r = getDrawingArea();
Double cw = getDrawing() == null ? null : getDrawing().get(CANVAS_WIDTH);
Double ch = getDrawing() == null ? null : getDrawing().get(CANVAS_HEIGHT);
Insets insets = getInsets();
if (cw == null || ch == null) {
cachedPreferredSize = new Dimension(
(int) Math.ceil((Math.max(0, r.x) + r.width)) + insets.left + insets.right,
(int) Math.ceil((Math.max(0, r.y) + r.height)) + insets.top + insets.bottom);
} else {
cachedPreferredSize = new Dimension(
(int) Math.ceil((-Math.min(0, r.x) + Math.max(Math.max(0, r.x) + r.width + Math.min(0, r.x), cw))) + insets.left + insets.right,
(int) Math.ceil((-Math.min(0, r.y) + Math.max(Math.max(0, r.y) + r.height + Math.min(0, r.y), ch))) + insets.top + insets.bottom);
}
}
return (Dimension) cachedPreferredSize.clone();
}
protected Rectangle2D.Double getDrawingArea() {
if (cachedDrawingArea == null) {
if (drawing != null) {
cachedDrawingArea = drawing.getDrawingArea();
} else {
cachedDrawingArea = new Rectangle2D.Double();
}
}
return (Rectangle2D.Double) cachedDrawingArea.clone();
}
/**
* Side effect: Changes view Translation.
*/
@Override
public void setBounds(int x, int y, int width, int height) {
super.setBounds(x, y, width, height);
validateViewTranslation();
}
/**
* Updates the view translation taking into account the current dimension
* of the view JComponent, the size of the drawing, and the scale factor.
*/
protected void validateViewTranslation() {
}
/**
* Converts drawing coordinates to view coordinates.
*/
@Override
public Point drawingToView(
Point2D.Double p) {
Point2D po = preTransform.transform(p, null);
return new Point((int) po.getX(), (int) po.getY());
}
@Override
public Rectangle drawingToView(
Rectangle2D.Double r) {
double[] drawing = {r.x, r.y, r.x + r.width, r.y, r.x + r.width, r.y + r.height, r.x, r.y + r.height};
double[] view = new double[8];
preTransform.transform(drawing, 0, view, 0, 4);
int x1 = Math.min((int)view[0], (int)view[2]);
x1 = Math.min(x1, (int)view[4]);
x1 = Math.min(x1, (int)view[6]);
int x2 = Math.max((int)view[0], (int)view[2]);
x2 = Math.max(x2, (int)view[4]);
x2 = Math.max(x2, (int)view[6]);
int y1 = Math.min((int)view[1], (int)view[3]);
y1 = Math.min(y1, (int)view[5]);
y1 = Math.min(y1, (int)view[7]);
int y2 = Math.max((int)view[1], (int)view[3]);
y2 = Math.max(y2, (int)view[5]);
y2 = Math.max(y2, (int)view[7]);
return new Rectangle(x1, y1, x2 - x1, y2 - y1);
}
/**
* Converts view coordinates to drawing coordinates.
*/
@Override
public Point2D.Double viewToDrawing(Point p) {
Point2D point = null;
try {
point = preTransform.inverseTransform(new Point2D.Double((double)p.x, (double)p.y), null);
} catch (NoninvertibleTransformException e) {
e.printStackTrace();
}
return (Point2D.Double) point;
}
@Override
public Rectangle2D.Double viewToDrawing(Rectangle r) {
double[] drawing = {r.x, r.y, r.x + r.width, r.y, r.x + r.width, r.y + r.height, r.x, r.y + r.height};
double[] view = new double[8];
//preTransform.transform(drawing, 0, view, 0, 4);
try {
preTransform.inverseTransform(drawing, 0, view, 0, 4);
} catch (NoninvertibleTransformException e) {
e.printStackTrace();
}
double x1 = Math.min(view[0], view[2]);
x1 = Math.min(x1, view[4]);
x1 = Math.min(x1, view[6]);
double x2 = Math.max(view[0], view[2]);
x2 = Math.max(x2, view[4]);
x2 = Math.max(x2, view[6]);
double y1 = Math.min(view[1], view[3]);
y1 = Math.min(y1, view[5]);
y1 = Math.min(y1, view[7]);
double y2 = Math.max(view[1], view[3]);
y2 = Math.max(y2, view[5]);
y2 = Math.max(y2, view[7]);
return new Rectangle2D.Double(x1, y1, x2 - x1, y2 - y1);
}
@Override
public AffineTransform getDrawingToViewTransform() {
AffineTransform t = new AffineTransform();
t.setTransform(preTransform);
return t;
}
@Override
public JComponent getComponent() {
return this;
}
@Override
public double getScaleFactor() {
return 1;
}
@Override
public void setScaleFactor(double newValue) {
double oldValue = 1;
//scaleFactor = newValue;
validateViewTranslation();
invalidateHandles();
revalidate();
repaint();
firePropertyChange("scaleFactor", oldValue, newValue);
}
protected void fireViewTransformChanged() {
for (Handle handle : selectionHandles) {
handle.viewTransformChanged();
}
for (Handle handle : secondaryHandles) {
handle.viewTransformChanged();
}
}
@Override
public void setHandleDetailLevel(int newValue) {
if (newValue != detailLevel) {
detailLevel = newValue;
invalidateHandles();
validateHandles();
}
}
@Override
public int getHandleDetailLevel() {
return detailLevel;
}
@Override
public void delete() {
final java.util.List<Figure> deletedFigures = drawing.sort(getSelectedFigures());
// Abort, if not all of the selected figures may be removed from the
// drawing
for (Figure f : deletedFigures) {
if (!f.isRemovable()) {
getToolkit().beep();
return;
}
}
// Get z-indices of deleted figures
final int[] deletedFigureIndices = new int[deletedFigures.size()];
for (int i = 0; i
< deletedFigureIndices.length; i++) {
deletedFigureIndices[i] = drawing.indexOf(deletedFigures.get(i));
}
clearSelection();
getDrawing().removeAll(deletedFigures);
getDrawing().fireUndoableEditHappened(new AbstractUndoableEdit() {
@Override
public String getPresentationName() {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
return labels.getString("edit.delete.text");
}
@Override
public void undo() throws CannotUndoException {
super.undo();
clearSelection();
Drawing d = getDrawing();
for (int i = 0; i
< deletedFigureIndices.length; i++) {
d.add(deletedFigureIndices[i], deletedFigures.get(i));
}
addToSelection(deletedFigures);
}
@Override
public void redo() throws CannotRedoException {
super.redo();
for (int i = 0; i
< deletedFigureIndices.length; i++) {
drawing.remove(deletedFigures.get(i));
}
}
});
}
@Override
public void duplicate() {
Collection<Figure> sorted = getDrawing().sort(getSelectedFigures());
HashMap<Figure, Figure> originalToDuplicateMap = new HashMap<Figure, Figure>(sorted.size());
clearSelection();
final ArrayList<Figure> duplicates = new ArrayList<Figure>(sorted.size());
AffineTransform tx = new AffineTransform();
tx.translate(5, 5);
for (Figure f : sorted) {
Figure d = (Figure) f.clone();
d.transform(tx);
duplicates.add(d);
originalToDuplicateMap.put(f, d);
drawing.add(d);
}
for (Figure f : duplicates) {
f.remap(originalToDuplicateMap, false);
}
addToSelection(duplicates);
getDrawing().fireUndoableEditHappened(new AbstractUndoableEdit() {
@Override
public String getPresentationName() {
ResourceBundleUtil labels = ResourceBundleUtil.getBundle("org.jhotdraw.draw.Labels");
return labels.getString("edit.duplicate.text");
}
@Override
public void undo() throws CannotUndoException {
super.undo();
getDrawing().removeAll(duplicates);
}
@Override
public void redo() throws CannotRedoException {
super.redo();
getDrawing().addAll(duplicates);
}
});
}
@Override
public void removeNotify(DrawingEditor editor) {
this.editor = null;
repaint();
}
@Override
public void addNotify(DrawingEditor editor) {
DrawingEditor oldValue = editor;
this.editor = editor;
firePropertyChange("editor", oldValue, editor);
invalidateHandles();
repaint();
}
@Override
public void setVisibleConstrainer(Constrainer newValue) {
Constrainer oldValue = visibleConstrainer;
visibleConstrainer =
newValue;
firePropertyChange(VISIBLE_CONSTRAINER_PROPERTY, oldValue, newValue);
}
@Override
public Constrainer getVisibleConstrainer() {
return visibleConstrainer;
}
@Override
public void setInvisibleConstrainer(Constrainer newValue) {
Constrainer oldValue = invisibleConstrainer;
invisibleConstrainer =
newValue;
firePropertyChange(INVISIBLE_CONSTRAINER_PROPERTY, oldValue, newValue);
}
@Override
public Constrainer getInvisibleConstrainer() {
return invisibleConstrainer;
}
@Override
public void setConstrainerVisible(boolean newValue) {
boolean oldValue = isConstrainerVisible;
isConstrainerVisible =
newValue;
firePropertyChange(CONSTRAINER_VISIBLE_PROPERTY, oldValue, newValue);
repaint();
}
@Override
public boolean isConstrainerVisible() {
return isConstrainerVisible;
}
/** Returns true, if the the drawing is double buffered.
*/
public boolean isDrawingDoubleBuffered() {
return isDrawingDoubleBuffered;
}
/**
* Returns a paint for drawing the background of the drawing area.
* @return Paint.
*/
protected Paint getBackgroundPaint(
int x, int y) {
if (backgroundTile == null) {
backgroundTile = new BufferedImage(16, 16, BufferedImage.TYPE_INT_RGB);
Graphics2D g = backgroundTile.createGraphics();
g.setColor(Color.white);
g.fillRect(0, 0, 16, 16);
g.setColor(new Color(0xdfdfdf));
g.fillRect(0, 0, 8, 8);
g.fillRect(8, 8, 8, 8);
g.dispose();
}
return new TexturePaint(backgroundTile,
new Rectangle(x, y, backgroundTile.getWidth(), backgroundTile.getHeight()));
}
@Override
public DrawingEditor getEditor() {
return editor;
}
// Variables declaration - do not modify//GEN-BEGIN:variables
// End of variables declaration//GEN-END:variables
@Override
public void setActiveHandle(@Nullable Handle newValue) {
Handle oldValue = activeHandle;
if (oldValue != null) {
repaint(oldValue.getDrawingArea());
}
activeHandle = newValue;
if (newValue != null) {
repaint(newValue.getDrawingArea());
}
firePropertyChange(ACTIVE_HANDLE_PROPERTY, oldValue, newValue);
}
@Override
public Handle getActiveHandle() {
return activeHandle;
}
}