/*
*------------------------------------------------------------------------------
* Copyright (C) 2006-2014 University of Dundee. All rights reserved.
*
*
* 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 2 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, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*------------------------------------------------------------------------------
*/
package org.openmicroscopy.shoola.agents.measurement.view;
import java.awt.Color;
import java.awt.Frame;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.event.WindowFocusListener;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.Map.Entry;
import javax.swing.JMenu;
import javax.swing.WindowConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuKeyEvent;
import javax.swing.event.MenuKeyListener;
import javax.swing.event.MenuListener;
import org.jhotdraw.draw.DrawingEvent;
import org.jhotdraw.draw.DrawingListener;
import org.jhotdraw.draw.Figure;
import org.jhotdraw.draw.FigureEvent;
import org.jhotdraw.draw.FigureListener;
import org.jhotdraw.draw.FigureSelectionEvent;
import org.jhotdraw.draw.FigureSelectionListener;
import org.jhotdraw.draw.AttributeKey;
import org.openmicroscopy.shoola.agents.events.measurement.SelectChannel;
import org.openmicroscopy.shoola.agents.measurement.MeasurementAgent;
import org.openmicroscopy.shoola.agents.measurement.actions.CreateFigureAction;
import org.openmicroscopy.shoola.agents.measurement.actions.DeleteROIAction;
import org.openmicroscopy.shoola.agents.measurement.actions.LoadROIAction;
import org.openmicroscopy.shoola.agents.measurement.actions.MeasurementViewerAction;
import org.openmicroscopy.shoola.agents.measurement.actions.ExportGraphAction;
import org.openmicroscopy.shoola.agents.measurement.actions.SaveROIAction;
import org.openmicroscopy.shoola.agents.measurement.actions.ShowROIAssistant;
import org.openmicroscopy.shoola.agents.measurement.actions.UnitsAction;
import org.openmicroscopy.shoola.agents.util.SelectionWizard;
import org.openmicroscopy.shoola.agents.util.ui.PermissionMenu;
import org.openmicroscopy.shoola.env.event.EventBus;
import org.openmicroscopy.shoola.util.roi.figures.MeasureLineFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasurePointFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureTextFigure;
import org.openmicroscopy.shoola.util.roi.figures.ROIFigure;
import org.openmicroscopy.shoola.util.roi.model.ROI;
import org.openmicroscopy.shoola.util.roi.model.ROIShape;
import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKeys;
import org.openmicroscopy.shoola.util.roi.model.annotation.MeasurementAttributes;
import org.openmicroscopy.shoola.util.roi.model.util.Coord3D;
import org.openmicroscopy.shoola.util.ui.LoadingWindow;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.colourpicker.ColourPicker;
import omero.gateway.model.AnnotationData;
import omero.gateway.model.TagAnnotationData;
/**
* The MeasurementViewer's Controller.
*
* @author Jean-Marie Burel
* <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
* @author Donald MacDonald
* <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a>
* @version 3.0
* <small>
* (<b>Internal version:</b> $Revision: $Date: $)
* </small>
* @since OME3.0
*/
class MeasurementViewerControl
implements ChangeListener, DrawingListener,
FigureListener, FigureSelectionListener, PropertyChangeListener,
WindowFocusListener, KeyListener
{
/** Identifies the <code>SAVE</code> action in the menu. */
static final Integer SAVE = Integer.valueOf(0);
/** Identifies the <code>LOAD</code> action in the menu. */
static final Integer LOAD = Integer.valueOf(1);
/** Identifies the <code>ROI Assistant</code> action in the menu. */
static final Integer ROI_ASSISTANT = Integer.valueOf(5);
/**
* Identifies the <code>showMeasurementinMicrons</code> action in
* the menu.
*/
static final Integer IN_MICRONS = Integer.valueOf(6);
/**
* Identifies the <code>showMeasurementinMicrons</code> action in
* the menu.
*/
static final Integer IN_PIXELS = Integer.valueOf(7);
/**
* Identifies the <code>createSingleFigure</code> action in
* the menu.
*/
static final Integer CREATE_SINGLE_FIGURE = Integer.valueOf(8);
/**
* Identifies the <code>create multiples figures</code> action in
* the menu.
*/
static final Integer CREATE_MULTIPLE_FIGURES = Integer.valueOf(9);
/** Identifies the <code>DELETE</code> action in the menu. */
static final Integer DELETE = Integer.valueOf(13);
/** Identifies the <code>EXPORT_GRAPH</code> action in the menu. */
static final Integer EXPORT_GRAPH = Integer.valueOf(14);
/**
* Reference to the {@link MeasurementViewer} component, which,
* in this context, is regarded as the Model.
*/
private MeasurementViewer model;
/** Reference to the View. */
private MeasurementViewerUI view;
/** Maps actions identifiers onto actual <code>Action</code> object. */
private Map<Integer, MeasurementViewerAction> actionsMap;
/** Flag indicating that the shape is removed via the delete key.*/
private boolean keyRemove;
/** Helper method to create all the UI actions. */
private void createActions()
{
actionsMap.put(SAVE, new SaveROIAction(model));
actionsMap.put(LOAD, new LoadROIAction(model));
actionsMap.put(ROI_ASSISTANT, new ShowROIAssistant(model));
actionsMap.put(IN_MICRONS, new UnitsAction(model, true));
actionsMap.put(IN_PIXELS, new UnitsAction(model, false));
actionsMap.put(CREATE_SINGLE_FIGURE,
new CreateFigureAction(model, true));
actionsMap.put(CREATE_MULTIPLE_FIGURES, new CreateFigureAction(model,
false));
actionsMap.put(DELETE, new DeleteROIAction(model));
actionsMap.put(EXPORT_GRAPH, new ExportGraphAction(model));
}
/**
* Return <code>true</code> if the right button was clicked or
* left button was clicked with control held down,
* <code>false</code> otherwise.
*
* @param e The mouse event to handle.
* @return See above.
*/
public static boolean isRightClick(MouseEvent e)
{
return (e.getButton() == MouseEvent.BUTTON3 ||
(e.getButton() == MouseEvent.BUTTON1 && e.isControlDown()));
//return SwingUtilities.isRightMouseButton(e);
}
/**
* Sets the status of the currently selected Region of Interest.
*
* @param status The value to set.
*/
private void setROIFigureStatus(int status)
{
Collection<Figure> selectedFigures =
view.getDrawingView().getSelectedFigures();
if (selectedFigures.size() != 1) return;
Iterator<Figure> i = selectedFigures.iterator();
Figure fig;
ROIFigure roiFigure;
while (i.hasNext()) {
fig = i.next();
if (fig instanceof ROIFigure) {
roiFigure = (ROIFigure) fig;
roiFigure.setStatus(status);
handleFigureChange(roiFigure);
}
}
}
/**
* Handles the selection of new Region of Interest.
*
* @param figure The value to handle.
*/
private void handleFigureChange(ROIFigure figure)
{
view.onSelectedFigures();
ROI roi = figure.getROI();
//TODO clean that code
if ((figure instanceof MeasureLineFigure) ||
(figure instanceof MeasurePointFigure)) {
figure.calculateMeasurements();
view.refreshResultsTable();
if (!view.inDataView()) return;
ROIShape shape = figure.getROIShape();
List<ROIShape> shapeList = new ArrayList<ROIShape>();
roi = shape.getROI();
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Iterator<Coord3D> shapeIterator = shapeMap.keySet().iterator();
while (shapeIterator.hasNext())
shapeList.add(shapeMap.get(shapeIterator.next()));
if (shapeList.size() != 0)
model.analyseShapeList(shapeList);
return;
}
if (figure.getStatus() != ROIFigure.IDLE) return;
figure.calculateMeasurements();
view.refreshResultsTable();
if (!view.inDataView()) return;
ROIShape shape = figure.getROIShape();
List<ROIShape> shapeList = new ArrayList<ROIShape>();
roi = shape.getROI();
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Iterator<Coord3D> shapeIterator = shapeMap.keySet().iterator();
ROIShape thisShape;
while (shapeIterator.hasNext())
{
thisShape = shapeMap.get(shapeIterator.next());
shapeList.add(thisShape);
}
if (shapeList.size() != 0)
model.analyseShapeList(shapeList);
}
/**
* Creates a new instance.
* The {@link #initialize(MeasurementViewer, MeasurementViewerUI) initialize}
* method should be called straight after to link this Controller
* to the other MVC components.
*/
MeasurementViewerControl() {}
/**
* Links this Controller to its Model and its View.
*
* @param model Reference to the {@link MeasurementViewer} component,
* which, in this context, is regarded as the Model.
* Mustn't be <code>null</code>.
* @param view Reference to the View. Mustn't be <code>null</code>.
*/
void initialize(MeasurementViewer model, MeasurementViewerUI view)
{
if (model == null) throw new NullPointerException("No model.");
if (view == null) throw new NullPointerException("No view.");
this.model = model;
this.view = view;
model.addChangeListener(this);
model.addPropertyChangeListener(this);
actionsMap = new HashMap<Integer, MeasurementViewerAction>();
createActions();
}
/**
* Attaches a window listener to the view to discard the model when
* the user closes the window.
*/
void attachListeners()
{
view.getLoadingWindow().addPropertyChangeListener(
LoadingWindow.CLOSED_PROPERTY, this);
view.getDrawing().addDrawingListener(this);
view.getDrawingView().addFigureSelectionListener(this);
view.getDrawingView().addKeyListener(this);
view.setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE);
view.addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent e) { model.close(); }
public void windowOpened(WindowEvent e) {
view.addWindowFocusListener(this); }
});
view.getDrawingView().addMouseListener(new MouseAdapter() {
public void mouseReleased(MouseEvent e)
{
setROIFigureStatus(ROIFigure.IDLE);
if (!UIUtilities.isWindowsOS()) {
if (isRightClick(e)) {
Collection l =
view.getDrawingView().getSelectedFigures();
if (l != null && l.size() == 1)
view.showROIManagementMenu(e.getX(), e.getY());
}
}
}
public void mousePressed(MouseEvent e)
{
setROIFigureStatus(ROIFigure.IDLE);
if (UIUtilities.isWindowsOS()) {
if (isRightClick(e)) {
Collection l =
view.getDrawingView().getSelectedFigures();
if (l != null && l.size() == 1)
view.showROIManagementMenu(e.getX(), e.getY());
}
}
}
});
view.getDrawingView().addMouseMotionListener(new MouseMotionAdapter()
{
@Override
public void mouseDragged(MouseEvent e)
{
if (isRightClick(e)) return;
setROIFigureStatus(ROIFigure.MOVING);
}
});
JMenu menu = MeasurementViewerFactory.getWindowMenu();
menu.addMenuListener(new MenuListener() {
public void menuSelected(MenuEvent e)
{
Object source = e.getSource();
if (source instanceof JMenu)
MeasurementViewerFactory.register((JMenu) source);
}
/**
* Required by I/F but not actually needed in our case,
* no-operation implementation.
* @see MenuListener#menuCanceled(MenuEvent)
*/
public void menuCanceled(MenuEvent e) {}
/**
* Required by I/F but not actually needed in our case,
* no-operation implementation.
* @see MenuListener#menuDeselected(MenuEvent)
*/
public void menuDeselected(MenuEvent e) {}
});
//Listen to keyboard selection
menu.addMenuKeyListener(new MenuKeyListener() {
public void menuKeyReleased(MenuKeyEvent e)
{
Object source = e.getSource();
if (source instanceof JMenu)
MeasurementViewerFactory.register((JMenu) source);
}
/**
* Required by I/F but not actually needed in our case,
* no-operation implementation.
* @see MenuKeyListener#menuKeyPressed(MenuKeyEvent)
*/
public void menuKeyPressed(MenuKeyEvent e) {}
/**
* Required by I/F but not actually needed in our case,
* no-operation implementation.
* @see MenuKeyListener#menuKeyTyped(MenuKeyEvent)
*/
public void menuKeyTyped(MenuKeyEvent e) {}
});
MeasurementViewerFactory.attachWindowMenuToTaskBar();
}
/**
* Moves the view to the front, to avoid loops, first removes the
* WindowFocusListener.
*/
void toFront()
{
if (view.getExtendedState() != Frame.NORMAL) return;
if (!view.isFocused()) {
view.removeWindowFocusListener(this);
//view.requestFocus();
if (view.isVisible())
view.setVisible(true);
//view.addWindowFocusListener(this);
}
}
/**
* Returns the action corresponding to the specified id.
*
* @param id One of the flags defined by this class.
* @return The specified action.
*/
MeasurementViewerAction getAction(Integer id) { return actionsMap.get(id); }
/**
* Brings up on screen the {@link ColourPicker}.
*
* @param color The color to pass to the {@link ColourPicker}.
*/
void showColorPicker(Color color)
{
if (color == null) return;
ColourPicker picker = new ColourPicker(view, color);
picker.addPropertyChangeListener(ColourPicker.COLOUR_PROPERTY, this);
UIUtilities.setLocationRelativeTo(view, picker);
}
/** Analyzes the selected figures. */
void analyseSelectedFigures()
{
analyseFigures(model.getSelectedFigures());
}
/**
* Analyzes the selected figures.
* @param figures The figures to analyse
*/
void analyseFigures(Collection<Figure> figures)
{
if (figures.size() != 1 && !view.inDataView())
return;
Iterator<Figure> j = figures.iterator();
ROIFigure figure = null;
if (j.hasNext()) figure = (ROIFigure) j.next();
if (figure == null) return;
ROIShape shape = figure.getROIShape();
List<ROIShape> shapeList = new ArrayList<ROIShape>();
ROI roi = shape.getROI();
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Entry entry;
Iterator i = shapeMap.entrySet().iterator();
ROIShape currentShape;
Coord3D c;
while (i.hasNext())
{
entry = (Entry) i.next();
c = (Coord3D) entry.getKey();
currentShape = (ROIShape) entry.getValue();
if (!(currentShape.getFigure() instanceof MeasureTextFigure))
shapeList.add(currentShape);
}
if (shapeList.size() != 0) model.analyseShapeList(shapeList);
}
/** Loads the tags.*/
void loadTags()
{
model.loadTags();
}
/**
* Reacts to property change.
* @see PropertyChangeListener#propertyChange(PropertyChangeEvent)
*/
public void propertyChange(PropertyChangeEvent evt)
{
String name = evt.getPropertyName();
if (ColourPicker.COLOUR_PROPERTY.equals(name))
view.setCellColor((Color) evt.getNewValue());
else if (LoadingWindow.CLOSED_PROPERTY.equals(name))
model.discard();
else if (PermissionMenu.SELECTED_LEVEL_PROPERTY.equals(name)) {
model.deleteAllROIs((Integer) evt.getNewValue());
} else if (SelectionWizard.SELECTED_ITEMS_PROPERTY.equals(name)) {
Map m = (Map) evt.getNewValue();
if (m == null || m.size() != 1)
return;
Set set = m.entrySet();
Entry entry;
Iterator i = set.iterator();
Class type;
while (i.hasNext()) {
entry = (Entry) i.next();
type = (Class) entry.getKey();
if (TagAnnotationData.class.getName().equals(type.getName())) {
List<AnnotationData> tags =
(List<AnnotationData>) entry.getValue();
model.tagSelectedFigures(tags);
}
}
}
}
/**
* Reacts to state changes in the {@link MeasurementViewer}.
* @see ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e)
{
int state = model.getState();
switch (state) {
case MeasurementViewer.ANALYSE_SHAPE:
view.setStatus("Analysing Shape.");
view.onAnalysed(true);
break;
case MeasurementViewer.LOADING_DATA:
LoadingWindow w = view.getLoadingWindow();
if (!w.isVisible()) UIUtilities.centerAndShow(w);
view.setStatus("Loading.");
break;
case MeasurementViewer.READY:
view.getLoadingWindow().setVisible(false);
view.setStatus("Ready.");
view.onAnalysed(false);
if(!view.isVisible())
view.setOnScreen();
break;
case MeasurementViewer.DISCARDED:
LoadingWindow window = view.getLoadingWindow();
window.setVisible(false);
window.dispose();
view.setVisible(false);
view.dispose();
}
}
/**
* Adds a new figure.
* @see DrawingListener#figureAdded(DrawingEvent)
*/
public void figureAdded(DrawingEvent e)
{
Figure f = e.getFigure();
if (!(f instanceof ROIFigure)) return;
ROIFigure roiFigure = (ROIFigure) f;
roiFigure.setStatus(ROIFigure.MOVING);
view.addROI(roiFigure);
if (roiFigure.canEdit())
roiFigure.addFigureListener(this);
model.setDataChanged();
if (!view.inDataView()) return;
ROIShape shape = roiFigure.getROIShape();
List<ROIShape> shapeList = new ArrayList<ROIShape>();
ROI roi = shape.getROI();
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Iterator<Coord3D> shapeIterator = shapeMap.keySet().iterator();
while (shapeIterator.hasNext())
shapeList.add(shapeMap.get(shapeIterator.next()));
if (shapeList.size() == 0) return;
if ((f instanceof MeasureLineFigure) ||
(f instanceof MeasurePointFigure)) {
roiFigure.setStatus(ROIFigure.IDLE);
model.analyseShapeList(shapeList);
}
}
/**
* Removes the selected figure from the display.
* @see DrawingListener#figureRemoved(DrawingEvent)
*/
public void figureRemoved(DrawingEvent e)
{
if (model.getState() != MeasurementViewer.READY) return;
Figure f = e.getFigure();
if (f instanceof ROIFigure) {
ROIFigure roi = (ROIFigure) f;
if (keyRemove) {
if (roi.isReadOnly() || !roi.canDelete()) {
view.getDrawing().removeDrawingListener(this);
view.getDrawing().add(roi);
view.getDrawing().addDrawingListener(this);
return;
}
view.markROIForDelete(roi);
keyRemove = false;
}
view.removeROI(roi);
model.setDataChanged();
}
}
/**
* Displays the selected figures.
* @see FigureSelectionListener#selectionChanged(FigureSelectionEvent)
*/
public void selectionChanged(FigureSelectionEvent evt)
{
// ignore events if viewer is 'closed'
if (!view.isVisible())
return;
Collection<Figure> figures = evt.getView().getSelectedFigures();
if (figures == null) return;
final List<ROIShape> shapeList = new ArrayList<ROIShape>();
if (view.inDataView() && figures.size() == 1) {
ROIFigure figure = (ROIFigure) figures.iterator().next();
if (figure == null) return;
ROIShape shape = figure.getROIShape();
if (shape == null) return;
ROI roi = shape.getROI();
if (roi == null) return;
TreeMap<Coord3D, ROIShape> shapeMap = roi.getShapes();
Iterator<Coord3D> shapeIterator = shapeMap.keySet().iterator();
while (shapeIterator.hasNext())
shapeList.add(shapeMap.get(shapeIterator.next()));
}
view.setSelectedFigures(figures);
if (!shapeList.isEmpty())
/* delayed because setSelectedFigures cannot work during shape analysis */
model.analyseShapeList(shapeList);
}
/**
* Required by the {@link DrawingListener} I/F.
* This allows the viewer to manage the link between figure attributes and
* ROIShape, ROI objects.
* @see FigureListener#figureAttributeChanged(FigureEvent)
*/
public void figureAttributeChanged(FigureEvent e)
{
Figure f = e.getFigure();
if (f instanceof ROIFigure)
{
ROIFigure fig = (ROIFigure) f;
view.onAttributeChanged(fig);
view.refreshInspectorTable();
model.figureAttributeChanged(e.getAttribute(), fig);
if (!fig.isReadOnly()) {
if (fig.canEdit()) {
AttributeKey<?> key = e.getAttribute();
if (key != MeasurementAttributes.SHOWTEXT &&
key != MeasurementAttributes.SHOWMEASUREMENT &&
key != AnnotationKeys.TAG) {
model.setDataChanged();
}
}
}
}
}
/**
* Required by the {@link DrawingListener} I/F used to update
* the measurements of a component and the different dataviews.
* @see FigureListener#figureChanged(FigureEvent)
*/
public void figureChanged(FigureEvent e)
{
Figure f = e.getFigure();
if (f instanceof ROIFigure) {
ROIFigure fig = (ROIFigure) f;
Coord3D coord = fig.getROIShape().getCoord3D();
int c = coord.getChannel();
if (c >= 0 && !view.isChannelActive(c)) {
EventBus bus = MeasurementAgent.getRegistry().getEventBus();
bus.post(new SelectChannel(view.getPixelsID(), c));
}
handleFigureChange(fig);
}
}
/**
* Calculates the statistics for the selected shape.
* @see KeyListener#keyPressed(KeyEvent)
*/
public void keyPressed(KeyEvent e)
{
keyRemove = false;
if (e.getKeyCode() == KeyEvent.VK_DELETE)
keyRemove = true;
char ANALYSECHAR = 'a';
if (e.getKeyChar() == ANALYSECHAR) {
Collection<Figure> selectedFigures =
view.getDrawingView().getSelectedFigures();
if (selectedFigures.size() != 1) return;
Iterator<Figure> iterator = selectedFigures.iterator();
ROIFigure fig = (ROIFigure) iterator.next();
if (fig instanceof MeasureTextFigure) return;
List<ROIShape> shapeList = new ArrayList<ROIShape>();
shapeList.add(fig.getROIShape());
view.calculateStats(shapeList);
}
}
/**
* Posts an event to bring the related window to the front.
* @see WindowFocusListener#windowGainedFocus(WindowEvent)
*/
public void windowGainedFocus(WindowEvent e)
{
/*
EventBus bus = ImViewerAgent.getRegistry().getEventBus();
bus.post(new FocusGainedEvent(view.getPixelsID(),
FocusGainedEvent.MEASUREMENT_TOOL_FOCUS));
*/
}
/**
* Required by the I/F but no-operation implementation in our case.
* @see WindowFocusListener#windowLostFocus(WindowEvent)
*/
public void windowLostFocus(WindowEvent e) {}
/**
* Required by the {@link DrawingListener} I/F but no-operation
* implementation in our case.
* @see DrawingListener#areaInvalidated(DrawingEvent)
*/
public void areaInvalidated(DrawingEvent e) {}
/**
* Required by the {@link DrawingListener} I/F but no-operation
* implementation in our case.
* @see FigureListener#figureAdded(FigureEvent)
*/
public void figureAdded(FigureEvent e) {}
/**
* Required by the {@link DrawingListener} I/F but no-operation
* implementation in our case.
* @see FigureListener#figureAreaInvalidated(FigureEvent)
*/
public void figureAreaInvalidated(FigureEvent e) {}
/**
* Required by the {@link DrawingListener} I/F but no-operation
* implementation in our case.
* @see FigureListener#figureRemoved(FigureEvent)
*/
public void figureRemoved(FigureEvent e) {}
/**
* Required by the {@link DrawingListener} I/F but no-operation
* implementation in our case.
* @see FigureListener#figureRequestRemove(FigureEvent)
*/
public void figureRequestRemove(FigureEvent e) {}
/**
* Required by the {@link FigureListener} I/F but no-operation
* implementation in our case.
* @see FigureListener#figureHandlesChanged(FigureEvent)
*/
public void figureHandlesChanged(FigureEvent e) {}
/**
* Required by the {@link KeyListener} I/F but no-operation implementation
* in our case.
* @see KeyListener#keyPressed(KeyEvent)
*/
public void keyTyped(KeyEvent e) {}
/**
* Required by the {@link KeyListener} I/F but no-operation implementation
* in our case.
* @see KeyListener#keyReleased(KeyEvent)
*/
public void keyReleased(KeyEvent e) {}
}