/*
* org.openmicroscopy.shoola.agents.measurement.view.ObjectInspector
*
*------------------------------------------------------------------------------
* 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.BorderLayout;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListSelectionModel;
import javax.swing.table.TableCellEditor;
import org.apache.commons.lang.StringUtils;
import org.jhotdraw.draw.AttributeKey;
import org.jhotdraw.draw.TextHolderFigure;
import org.openmicroscopy.shoola.agents.measurement.IconManager;
import org.openmicroscopy.shoola.agents.measurement.MeasurementAgent;
import org.openmicroscopy.shoola.agents.measurement.util.model.AnnotationDescription;
import org.openmicroscopy.shoola.agents.measurement.util.model.AttributeField;
import org.openmicroscopy.shoola.agents.measurement.util.model.FigureTableModel;
import org.openmicroscopy.shoola.agents.measurement.util.ui.FigureTable;
import org.openmicroscopy.shoola.util.roi.figures.MeasureBezierFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureEllipseFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureLineConnectionFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureLineFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureRectangleFigure;
import org.openmicroscopy.shoola.util.roi.figures.MeasureTextFigure;
import org.openmicroscopy.shoola.util.roi.figures.ROIFigure;
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;
/**
* UI Component displaying various drawing information about a
* Region of Interest.
*
* @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
* @since OME3.0
*/
class ObjectInspector
extends JPanel
{
/** Index to identify tab */
public final static int INDEX = MeasurementViewerUI.INSPECTOR_INDEX;
/** Collection of column names. */
private static final List<String> COLUMN_NAMES;
/** The name of the panel. */
private static final String NAME = "Inspector";
/** Text indicating the scaling factor.*/
private static final String MAGNIFICATION = "The scaling Factor";
/** The table hosting the various fields. */
private FigureTable fieldTable;
/** Reference to the control. */
private MeasurementViewerControl controller;
/** Reference to the model. */
private MeasurementViewerModel model;
/** Magnification factor label*/
private JLabel infoLabel;
static {
COLUMN_NAMES = new ArrayList<String>(2);
COLUMN_NAMES.add("Field");
COLUMN_NAMES.add("Value");
}
/** Statically initialize the AttributeFields to be shown;
* The order in the list reflects the order they are shown in the table
*/
private static final List<AttributeField> attributeFields = new ArrayList<AttributeField>();
static {
attributeFields.add(new AttributeField(AnnotationKeys.TAG,
AnnotationDescription.annotationDescription
.get(AnnotationKeys.TAG), false));
attributeFields.add(new AttributeField(MeasurementAttributes.TEXT,
AnnotationDescription.annotationDescription
.get(AnnotationKeys.TEXT), true));
attributeFields.add(new AttributeField(MeasurementAttributes.FONT_SIZE,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.FONT_SIZE), true));
attributeFields.add(new AttributeField(MeasurementAttributes.SCALE_PROPORTIONALLY,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.SCALE_PROPORTIONALLY), false));
attributeFields.add(new AttributeField(MeasurementAttributes.WIDTH,
AnnotationDescription.annotationDescription
.get(AnnotationKeys.WIDTH), true));
attributeFields.add(new AttributeField(MeasurementAttributes.HEIGHT,
AnnotationDescription.annotationDescription
.get(AnnotationKeys.HEIGHT), true));
attributeFields.add(new AttributeField(MeasurementAttributes.SHOWTEXT,
"Show Comment", false));
attributeFields.add(new AttributeField(MeasurementAttributes.SHOWMEASUREMENT,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.SHOWMEASUREMENT), false));
attributeFields.add(new AttributeField(MeasurementAttributes.FILL_COLOR,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.FILL_COLOR), false));
attributeFields.add(new AttributeField(MeasurementAttributes.STROKE_COLOR,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.STROKE_COLOR), false));
attributeFields.add(new AttributeField(MeasurementAttributes.STROKE_WIDTH,
AnnotationDescription.annotationDescription
.get(MeasurementAttributes.STROKE_WIDTH), true));
}
/** Initializes the component composing the display. */
private void initComponents()
{
infoLabel = new JLabel(MAGNIFICATION+" is "+
model.getDrawingView().getScaleFactor());
//create the table
fieldTable = new FigureTable(new FigureTableModel(attributeFields,
COLUMN_NAMES));
fieldTable.getTableHeader().setReorderingAllowed(false);
fieldTable.setRowHeight(26);
fieldTable.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
fieldTable.setCellSelectionEnabled(true);
fieldTable.setColumnSelectionAllowed(true);
fieldTable.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e) {
int col = fieldTable.getSelectedColumn();
int row = fieldTable.getSelectedRow();
Object value = fieldTable.getValueAt(row, col);
if (e.getClickCount() == 1) {
if (value instanceof Boolean) {
toggleValue();
}
} else if (e.getClickCount() > 1) {
e.consume();
if (value instanceof Color) {
//Only if the figure is not read only.
FigureTableModel ftm = (FigureTableModel)
fieldTable.getModel();
ROIFigure figure = ftm.getFigure();
if (figure != null && !figure.isReadOnly())
if (figure.canEdit())
controller.showColorPicker((Color) value);
} else if (value instanceof Boolean) {
toggleValue();
}
}
}
});
fieldTable.addPropertyChangeListener(new PropertyChangeListener() {
public void propertyChange(PropertyChangeEvent evt) {
String name = evt.getPropertyName();
if (FigureTable.VALUE_CHANGED_PROPERTY.equals(name)) {
handleValueChanged((String) evt.getNewValue());
}
}
});
}
private void handleValueChanged(String text) {
int row = fieldTable.getEditingRow();
if (row < 0 || row >= attributeFields.size()) {
return;
}
AttributeKey attr = getAttributeKey(row);
FigureTableModel ftm = (FigureTableModel) fieldTable.getModel();
ROIFigure figure = ftm.getFigure();
if (attr.equals(MeasurementAttributes.TEXT)) {
if(TextHolderFigure.class.isAssignableFrom(figure.getClass())) {
((TextHolderFigure) figure).setText(text);
}
} else if (attr.equals(MeasurementAttributes.FONT_SIZE)) {
double d = parseDouble(text);
figure.setAttribute(MeasurementAttributes.FONT_SIZE, d);
figure.changed();
}
else if (attr.equals(MeasurementAttributes.STROKE_WIDTH)) {
double d = parseDouble(text);
figure.setAttribute(MeasurementAttributes.STROKE_WIDTH, d);
}
else if (attr.equals(MeasurementAttributes.WIDTH)) {
try {
double d = parseDouble(text);
setFigureDimension(figure, MeasurementAttributes.WIDTH, d);
if (isScaleProportionally()) {
setFigureDimension(figure, MeasurementAttributes.HEIGHT, d);
}
} catch (Exception e) {
}
} else if (attr.equals(MeasurementAttributes.HEIGHT)) {
try {
double d = parseDouble(text);
setFigureDimension(figure, MeasurementAttributes.HEIGHT, d);
if (isScaleProportionally()) {
setFigureDimension(figure, MeasurementAttributes.WIDTH, d);
}
} catch (Exception e) {
}
}
model.getDrawingView().repaint();
}
/**
* Safe method to parse text into double value
*
* @param text
* The text to convert to double
* @return The parsed double value or 1 if input is invalid or < 1
*/
private double parseDouble(String text) {
double result = 1;
NumberFormat nf = NumberFormat.getInstance();
if (StringUtils.isNotBlank(text)) {
try {
result = nf.parse(text).doubleValue();
} catch (ParseException e) {
}
if (result < 1) {
result = 1;
}
}
return result;
}
private void setFigureDimension(ROIFigure figure, AttributeKey key,
double dimension) {
if (figure instanceof MeasureEllipseFigure ||
figure instanceof MeasureRectangleFigure ||
figure instanceof MeasureBezierFigure ||
figure instanceof MeasureTextFigure ||
figure instanceof MeasureLineConnectionFigure ||
figure instanceof MeasureLineFigure) {
figure.setAttribute(key, dimension);
}
}
/** Toggles the value of the boolean under the current selection. */
private void toggleValue()
{
int col = fieldTable.getSelectedColumn();
int row = fieldTable.getSelectedRow();
Object v = (Boolean) fieldTable.getModel().getValueAt(row, col);
Boolean value = Boolean.valueOf(false);
if (v != null) value = (Boolean) v;
boolean newValue = !(value.booleanValue());
fieldTable.getModel().setValueAt(Boolean.valueOf(newValue), row, col);
model.getDrawingView().repaint();
}
/** Builds and lays out the UI. */
private void buildGUI()
{
setLayout(new BorderLayout());
add(new JScrollPane(fieldTable), BorderLayout.CENTER);
}
/**
* Creates a new instance.
*
* @param controller Reference to the Control. Mustn't be <code>null</code>.
* @param model Reference to the Model. Mustn't be <code>null</code>.
*/
ObjectInspector(MeasurementViewerControl controller,
MeasurementViewerModel model)
{
if (controller == null)
throw new IllegalArgumentException("No control.");
if (model == null)
throw new IllegalArgumentException("No model.");
this.controller = controller;
this.model = model;
initComponents();
buildGUI();
}
/**
* Returns the name of the component.
*
* @return See above.
*/
String getComponentName() { return NAME; }
/**
* Returns the icon of the component.
*
* @return See above.
*/
Icon getComponentIcon()
{
IconManager icons = IconManager.getInstance();
return icons.getIcon(IconManager.INSPECTOR);
}
/**
* Sets the passed color to the currently selected cell.
* Returns the selected row.
*
* @param c The color to set.
*/
int setCellColor(Color c)
{
int col = fieldTable.getSelectedColumn();
int row = fieldTable.getSelectedRow();
fieldTable.getModel().setValueAt(c, row, col);
return row;
}
/**
* Returns <code>true</code> if the text has to be shown,
* <code>false</code> otherwise.
*
* @return See above.
*/
boolean isShowText()
{
if (fieldTable == null) return false;
int n = fieldTable.getRowCount();
if (n > 4) {
Object v = fieldTable.getModel().getValueAt(getRowIndex(MeasurementAttributes.SHOWTEXT), 1);
if (v == null) return false;
return (Boolean) v;
}
return false;
}
/**
* Shows or hides the text for the currently selected figure.
*
* @param show Pass <code>true</code> to show the text, <code>false</code>
* otherwise.
* @param figure The selected figure.
*/
void showText(boolean show, ROIFigure figure)
{
if (fieldTable == null) return;
int n = fieldTable.getRowCount();
if (n > 4) {
FigureTableModel ftm = (FigureTableModel)
fieldTable.getModel();
ROIFigure f = ftm.getFigure();
if (f != null && f == figure)
fieldTable.getModel().setValueAt(show, getRowIndex(MeasurementAttributes.SHOWTEXT), 1);
}
}
/**
* Returns <code>true</code> if the measurement has to be shown,
* <code>false</code> otherwise.
*
* @return See above.
*/
boolean isShowMeasurement()
{
if (fieldTable == null) return false;
int n = fieldTable.getRowCount();
if (n > 5) {
Object v = fieldTable.getModel().getValueAt(getRowIndex(MeasurementAttributes.SHOWMEASUREMENT),
1);
if (v == null) return false;
return (Boolean) v;
}
return false;
}
/**
* Returns <code>true</code> if the figure dimensions are to be scaled
* proportionally, <code>false</code> otherwise.
*
* @return See above.
*/
boolean isScaleProportionally() {
if (fieldTable == null) return false;
int n = fieldTable.getRowCount();
if (n > 1) {
Object v = fieldTable.getModel().getValueAt(getRowIndex(MeasurementAttributes.SCALE_PROPORTIONALLY),
1);
if (v == null) return false;
return (Boolean) v;
}
return false;
}
/**
* Sets the data.
*
* @param figure The data to set.
*/
void setModelData(ROIFigure figure)
{
FigureTableModel tableModel = (FigureTableModel) fieldTable.getModel();
tableModel.setData(figure);
fieldTable.repaint();
}
/** Updates the display when the magnification factor changes.*/
void onMagnificationChanged()
{
infoLabel.setText(MAGNIFICATION+" is "+
model.getDrawingView().getScaleFactor());
}
/**
* Removes the ROI figure.
*
* @param figure The figure to remove.
*/
void removeROIFigure(ROIFigure figure)
{
if (figure == null) return;
FigureTableModel tm = (FigureTableModel) fieldTable.getModel();
ROIFigure value = tm.getFigure();
if (value == null) return;
if (value.getROI().getID() == figure.getROI().getID())
tm.clearData();
}
/**
* Removes the ROI figures.
*
* @param figures The figures to remove.
*/
void removeROIFigures(List<ROIFigure> figures)
{
if (figures == null || figures.size() == 0) return;
FigureTableModel tm = (FigureTableModel) fieldTable.getModel();
ROIFigure value = tm.getFigure();
if (value == null) return;
Iterator<ROIFigure> i = figures.iterator();
ROIFigure figure;
while (i.hasNext()) {
figure = i.next();
if (value.getROI().getID() == figure.getROI().getID())
tm.clearData();
}
fieldTable.repaint();
}
/**
* Sets the new figure retrieved from the passed collection.
*
* @param l The collection to handle.
*/
void setSelectedFigures(List<ROIShape> l)
{
FigureTableModel tableModel = (FigureTableModel) fieldTable.getModel();
Iterator<ROIShape> i = l.iterator();
//Register error and notify user.
ROIShape shape;
try {
TableCellEditor editor = fieldTable.getCellEditor();
if (editor != null) editor.stopCellEditing();
while (i.hasNext()) {
shape = i.next();
tableModel.setData(shape.getFigure());
fieldTable.repaint();
}
} catch (Exception e) {
MeasurementAgent.getRegistry().getLogger().info(this,
"Figures selection"+e);
}
}
/** Clear the inspector after saving the data. */
void clearData()
{
FigureTableModel tm = (FigureTableModel) fieldTable.getModel();
tm.clearData();
}
/**
* Get the AttributeKey which is handled by a certain row
* @param row The row which AttributeKey to get
* @return See above
*/
AttributeKey getAttributeKey(int row) {
return attributeFields.get(row).getKey();
}
/**
* Get the index of the row which handles a certain attribute
* @param attKey The AttributeKey
* @return See above
*/
int getRowIndex(AttributeKey attKey) {
for (int i = 0; i < attributeFields.size(); i++) {
if (attributeFields.get(i).getKey().equals(attKey)) {
return i;
}
}
return -1;
}
}