/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2016 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.Dimension; import java.awt.image.BufferedImage; import java.io.File; import java.io.InputStream; 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.TreeMap; import java.util.Map.Entry; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.filechooser.FileFilter; import org.apache.commons.collections.CollectionUtils; import org.jhotdraw.draw.AttributeKey; import org.jhotdraw.draw.Drawing; import org.jhotdraw.draw.Figure; import org.openmicroscopy.shoola.agents.events.measurement.MeasurementToolLoaded; import org.openmicroscopy.shoola.agents.events.measurement.ROIEvent; import org.openmicroscopy.shoola.agents.measurement.MeasurementAgent; import org.openmicroscopy.shoola.agents.measurement.util.FileMap; import org.openmicroscopy.shoola.agents.util.EditorUtil; import org.openmicroscopy.shoola.agents.util.SelectionWizard; import org.openmicroscopy.shoola.env.config.Registry; import org.openmicroscopy.shoola.env.data.model.DeletableObject; import omero.gateway.SecurityContext; import omero.gateway.model.ROIResult; import org.openmicroscopy.shoola.env.data.util.StructuredDataResults; import org.openmicroscopy.shoola.env.event.EventBus; import omero.log.LogMessage; import omero.log.Logger; import org.openmicroscopy.shoola.env.ui.UserNotifier; import org.openmicroscopy.shoola.util.ui.UIUtilities; import org.openmicroscopy.shoola.util.ui.component.AbstractComponent; import org.openmicroscopy.shoola.util.ui.filechooser.FileChooser; import org.openmicroscopy.shoola.util.filter.file.XMLFilter; import org.openmicroscopy.shoola.util.roi.exception.NoSuchROIException; import org.openmicroscopy.shoola.util.roi.exception.ParsingException; 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.ShapeList; import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKeys; import org.openmicroscopy.shoola.util.roi.model.util.Coord3D; import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.Multimap; import omero.gateway.model.AnnotationData; import omero.gateway.model.ChannelData; import omero.gateway.model.DataObject; import omero.gateway.model.ExperimenterData; import omero.gateway.model.FileAnnotationData; import omero.gateway.model.ROIData; import omero.gateway.model.ShapeData; import omero.gateway.model.TagAnnotationData; /** * Implements the {@link MeasurementViewer} interface to provide the * functionality required of the Measurement viewer component. * This class is the component hub and embeds the component's MVC triad. * It manages the component's state machine and fires state change * notifications as appropriate, but delegates actual functionality to the * MVC sub-components. * * @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerModel * @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerUI * @see org.openmicroscopy.shoola.agents.measurement.view.MeasurementViewerControl * * @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 MeasurementViewerComponent extends AbstractComponent implements MeasurementViewer { /** The Model sub-component. */ private MeasurementViewerModel model; /** The Control sub-component. */ private MeasurementViewerControl controller; /** The View sub-component. */ private MeasurementViewerUI view; /** * Posts an event to indicating to add or remove the component * from the display. * * @param index Either {@link MeasurementToolLoaded#ADD} or * {@link MeasurementToolLoaded#REMOVE}. */ private void postEvent(int index) { MeasurementToolLoaded response = new MeasurementToolLoaded( MeasurementViewerFactory.getRequest(model.getPixelsID()), model.getSecurityContext(), model.getDrawingView(), index); EventBus bus = MeasurementAgent.getRegistry().getEventBus(); bus.post(response); } /** * Creates a file chooser corresponding to the passed type. * * @param type The type of the file chooser. * @return See above. */ private FileChooser createChooserDialog(int type) { String word = "Save "; if (type == FileChooser.LOAD) word = "Load "; String title = word+"the ROI File"; String text = word+"the ROI data in the file associate with the image."; List<FileFilter> filters = new ArrayList<FileFilter>(); filters.add(new XMLFilter()); FileChooser chooser = new FileChooser(view, type, title, text, filters, false, true); try { File f = UIUtilities.getDefaultFolder(); if (f != null) chooser.setCurrentDirectory(f); } catch (Exception ex) {} try { String s = FileMap.getSavedFile(model.getServerName(), model.getUserName(), model.getPixelsID()); File savedFile; if (s != null) { savedFile = new File(s); chooser.setCurrentDirectory(savedFile); chooser.setSelectedFile(savedFile); } else { if (type == FileChooser.SAVE) { s = model.getImageName(); savedFile = new File(s); chooser.setSelectedFile(savedFile.getName()); } } } catch (ParsingException e) { // Do nothing as we're really only looking to see if the default // directory or filename should be set for loading. } return chooser; } /** * Creates a new instance. * The {@link #initialize() initialize} method should be called straigh * after to complete the MVC set up. * * @param model The Model sub-component. Mustn't be <code>null</code>. */ MeasurementViewerComponent(MeasurementViewerModel model) { if (model == null) throw new NullPointerException("No model."); this.model = model; controller = new MeasurementViewerControl(); view = new MeasurementViewerUI(model.getImageTitle()); } /** Links up the MVC triad. */ void initialize() { model.initialize(this); controller.initialize(this, view); view.initialize(this, controller, model); } /** * Returns the Model sub-component. * * @return See above. */ MeasurementViewerModel getModel() { return model; } /** Saves the ROI (not asynchronously) and discards. */ void saveAndDiscard() { model.saveROIToServer(false, false); discard(); } /** * Invokes when the ROI has been deleted. * * @param imageID The image's identifier. */ void onROIDeleted(long imageID) { model.onROIDeleted(imageID); fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#activate() */ public void activate() { activate(model.getMeasurements(), model.isHCSData(), model.isBigImage()); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#activate(List, boolean, boolean) */ public void activate(List<FileAnnotationData> measurements, boolean HCSData, boolean isBigImage) { int state = model.getState(); switch (state) { case NEW: //Sets the dimension of the drawing canvas; double f = model.getMagnification(); Dimension d = new Dimension((int) (model.getSizeX()*f), (int) (model.getSizeY()*f)); UIUtilities.setDefaultSize(model.getDrawingView(), d); model.getDrawingView().setSize(d); model.setHCSData(HCSData); model.setBigImage(isBigImage); view.buildGUI(); if (HCSData) { if (measurements == null) { model.setHCSData(false); model.fireLoadROIServerOrClient(false); } else model.fireLoadROIFromServer(measurements); } else { model.fireLoadROIServerOrClient(false); } break; case DISCARDED: throw new IllegalStateException( "This method can't be invoked in the DISCARDED state."); default: if (!view.isVisible()) postEvent(MeasurementToolLoaded.ADD); view.deIconify(); view.setVisible(true); } } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#getUI() */ public JFrame getUI() { return view; } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#discard() */ public void discard() { if (model.getState() != DISCARDED) { view.setVisible(false); model.discard(); fireStateChange(); } } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setDataChanged() */ public void setDataChanged() { model.notifyDataChanged(true); firePropertyChange(ROI_CHANGED_PROPERTY, Boolean.valueOf(false), Boolean.valueOf(true)); fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#cancel() */ public void cancel() { model.cancel(); view.setReadyStatus(); fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#getState() */ public int getState() { return model.getState(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setROI(InputStream) */ public void setROI(InputStream input) { //if (model.getState() != LOADING_ROI || input == null) return; if (model.getState() != LOADING_ROI) return; if (input == null) { model.setState(MeasurementViewer.READY); view.refreshToolBar(); view.rebuildManagerTable(); view.updateDrawingArea(); view.setReadyStatus(); fireStateChange(); //Now we are ready to go. We can post an event to add component to //Viewer postEvent(MeasurementToolLoaded.ADD); return; } Registry reg = MeasurementAgent.getRegistry(); Logger log = reg.getLogger(); try { boolean valid = model.setROI(input); if (!valid) { reg.getUserNotifier().notifyInfo("ROI", "The ROI are not " + "compatible with the image."); try { input.close(); } catch (Exception io) { log.warn(this, "Cannot close the stream "+io.getMessage()); } //reset fireStateChange(); return; } } catch (Exception e) { if (e instanceof ParsingException) { log.error(this, "Cannot parse the ROI for "+model.getImageID()); } else { } try { input.close(); } catch (Exception io) { log.warn(this, "Cannot close the stream "+io.getMessage()); } return; } view.refreshToolBar(); view.rebuildManagerTable(); view.updateDrawingArea(); view.setReadyStatus(); fireStateChange(); //Now we are ready to go. We can post an event to add component to //Viewer postEvent(MeasurementToolLoaded.ADD); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setMagnifiedPlane(int, int, double) */ public void setMagnifiedPlane(int defaultZ, int defaultT, double magnification) { int z = model.getDefaultZ(); int t = model.getDefaultT(); double f = model.getMagnification(); if (z == defaultZ && t == defaultT) { if (f != magnification) { model.setMagnification(magnification); view.onMagnificationChanged(); } if (!model.isBigImage()) return; } model.setPlane(defaultZ, defaultT); Drawing drawing = model.getDrawing(); drawing.removeDrawingListener(controller); drawing.clear(); ShapeList list = null; try { list = model.getShapeList(); } catch (Exception e) { view.handleROIException(e, MeasurementViewerUI.RETRIEVE_MSG); } view.setStatus(MeasurementViewerUI.DEFAULT_MSG); if (list != null) { TreeMap map = list.getList(); Iterator i = map.values().iterator(); ROIShape shape; ROIFigure fig; while (i.hasNext()) { shape = (ROIShape) i.next(); if (shape != null) { fig = shape.getFigure(); drawing.add(fig); if (fig.canAnnotate()) fig.addFigureListener(controller); } } } //Reset the result. view.displayAnalysisResults(); model.getDrawingView().setDrawing(drawing); drawing.addDrawingListener(controller); if (f != magnification) model.setMagnification(magnification); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#close() */ public void close() { if (model.getState() == DISCARDED) { return; } /* if (!model.isDataSaved()) { String title = "Discard Changes"; String message = "Do you want to exit and discard changes?"; MessageBox dialog = new MessageBox(view, title, message); if (dialog.showMsgBox() == MessageBox.NO_OPTION) return; } model.setDataDiscarded(); */ //Post event indicating that we don't care about saving. postEvent(MeasurementToolLoaded.REMOVE); if (model.isHCSData()) { List<FileAnnotationData> list = model.getMeasurements(); if (list == null || list.size() == 0) view.setVisible(false); else discard(); } else view.setVisible(false); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#iconified(boolean) */ public void iconified(boolean b) { if (model.getState() == DISCARDED) throw new IllegalStateException("This method shouldn't be " + "invoked in the DISCARDED state:"+model.getState()); view.setVisible(b); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#loadROI() */ public void loadROI() { FileChooser chooser = createChooserDialog(FileChooser.LOAD); chooser.setCheckOverride(false); if (chooser.showDialog() != JFileChooser.APPROVE_OPTION) return; File f = chooser.getSelectedFile(); if (f == null) return; model.fireROILoading(f.getAbsolutePath()); fireStateChange(); //view.updateDrawingArea(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#saveROI() */ public void saveROI() { FileChooser chooser = createChooserDialog(FileChooser.SAVE); if (chooser.showDialog() != JFileChooser.APPROVE_OPTION) return; File file = chooser.getSelectedFile(); if (file == null) return; String s = file.getAbsolutePath(); if (s == null || s.trim().length() == 0) return; if (!s.endsWith(XMLFilter.XML)) { String fileName = s+"."+XMLFilter.XML; file = new File(fileName); } saveBackROI(file.getAbsolutePath()); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#saveROIToServer(boolean) */ public void saveROIToServer(boolean close) { if (!canAnnotate()) return; List<ROI> l = model.getROIToDelete(); if (l != null && l.size() > 0) { List<DeletableObject> objects = new ArrayList<DeletableObject>(); Iterator<ROI> i = l.iterator(); ROI roi; ROIData data; SecurityContext ctx = model.getSecurityContext(); DeletableObject d; while (i.hasNext()) { roi = i.next(); if (!roi.isClientSide() && roi.canDelete()) { data = new ROIData(); data.setId(roi.getID()); data.setImage(model.getImage().asImage()); d = new DeletableObject(data); d.setSecurityContext(ctx); objects.add(d); } } if (objects.size() == 0) { model.saveROIToServer(true, close); } else { model.deleteAllROIs(objects); } } else { model.saveROIToServer(true, close); } fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#figureAttributeChanged(AttributeKey, ROIFigure) */ public void figureAttributeChanged(AttributeKey key, ROIFigure figure) { model.figureAttributeChanged(key, figure); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#showROIAssistant() */ public void showROIAssistant() { view.showROIAssistant(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#showROIAssistant(ROI) */ public void showROIAssistant(ROI roi) { view.showROIAssistant(roi); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#showMeasurementsInMicrons(boolean) */ public void showMeasurementsInMicrons(boolean inMicrons) { model.showMeasurementsInMicrons(inMicrons); view.updateDrawingArea(); view.refreshResultsTable(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setActiveChannels(Map) */ public void setActiveChannels(Map activeChannels) { int state = model.getState(); switch (model.getState()) { case DISCARDED: case LOADING_DATA: throw new IllegalStateException("This method cannot be " + "invoked in the DISCARDED, LOADING_DATA " + "state: "+state); } model.setActiveChannels(activeChannels); //Show or hide some shapes if they are visible on a channel or not TreeMap<Long, ROI> rois = model.getROI(); Collection<ROIFigure> figures = model.getAllFigures(); ROIFigure figure, f; ROI roi; TreeMap<Coord3D, ROIShape> shapeMap; ROIShape shape; Entry entry; if (rois != null) { Iterator j = rois.entrySet().iterator(); Iterator k; Coord3D coord; int c; while (j.hasNext()) { entry = (Entry) j.next(); roi = (ROI) entry.getValue(); shapeMap = roi.getShapes(); k = shapeMap.entrySet().iterator(); while (k.hasNext()) { entry = (Entry) k.next(); shape = (ROIShape) entry.getValue(); coord = shape.getCoord3D(); f = shape.getFigure(); c = coord.getChannel(); if (c >= 0) { if (f.canAnnotate()) { f.removeFigureListener(controller); f.setVisible(model.isChannelActive(c)); f.addFigureListener(controller); } } } } view.repaint(); } if (!view.inDataView() || !view.isVisible()) return; figures = getSelectedFigures(); if (figures.size() != 1) return; figure = figures.iterator().next(); List<ROIShape> shapeList = new ArrayList<ROIShape>(); roi = figure.getROI(); shapeMap = roi.getShapes(); Iterator j = shapeMap.entrySet().iterator(); while (j.hasNext()) { entry = (Entry) j.next(); shapeList.add( (ROIShape) entry.getValue()); } if (shapeList.size() != 0) analyseShapeList(shapeList); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setActiveChannelsColor(Map) */ public void setActiveChannelsColor(Map channels) { int state = model.getState(); switch (model.getState()) { case DISCARDED: case LOADING_DATA: throw new IllegalStateException("This method cannot be " + "invoked in the DISCARDED, LOADING_DATA " + "state: "+state); } model.setActiveChannels(channels); if (!view.inDataView() || !view.isVisible()) return; Collection<ROIFigure> collection = getSelectedFigures(); if (collection.size() != 1) return; ROIFigure figure = collection.iterator().next(); ArrayList<ROIShape> shapeList = new ArrayList<ROIShape>(); ROI roi = figure.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) analyseShapeList(shapeList); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setStatsShapes(Map) */ public void setStatsShapes(Map result) { int state = model.getState(); if (state != ANALYSE_SHAPE) { MeasurementAgent.getRegistry().getLogger().debug(this, "This method can only be invoked " + "in the ANALYSE_SHAPE state: "+state); return; } model.setAnalysisResults(result); view.displayAnalysisResults(); fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#analyseShapeList(List) */ public void analyseShapeList(List<ROIShape> shapeList) { if (shapeList == null) throw new IllegalArgumentException("No shape specified."); int state = model.getState(); switch (model.getState()) { case DISCARDED: case LOADING_DATA: case LOADING_ROI: throw new IllegalStateException("This method cannot be " + "invoked in the DISCARDED, LOADING_DATA or " + "LOADING_ROI state: "+state); case ANALYSE_SHAPE: return; } if (!validShapeList(shapeList)) return; if (model.getActiveChannels().size() == 0) { model.setAnalysisResults(null); view.displayAnalysisResults(); } else { model.fireAnalyzeShape(shapeList); fireStateChange(); } } /** * Check to see if the selected figure contains textFigure * @param shapeList see above. * @return see above. */ private boolean validShapeList(List<ROIShape> shapeList) { for(ROIShape shape : shapeList) if(shape.getFigure() instanceof MeasureTextFigure) return false; return true; } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#getSelectedFigures() */ public Collection getSelectedFigures() { return model.getSelectedFigures(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#createSingleFigure(boolean) */ public void createSingleFigure(boolean createSingleFig) { view.createSingleFigure(createSingleFig); } /** * Saves the ROI without displaying a file chooser. * * @param path The absolute path to the file. */ private void saveBackROI(String path) { Registry reg = MeasurementAgent.getRegistry(); UserNotifier un = reg.getUserNotifier(); try { model.saveROI(path, false); } catch (ParsingException e) { reg.getLogger().error(this, "Cannot save the ROI "+e.getMessage()); un.notifyInfo("Save ROI", "Cannot save ROI " + "for "+model.getImageID()); } un.notifyInfo("Save ROI", "The Regions of Interests have been " + "successfully saved. "); firePropertyChange(ROI_CHANGED_PROPERTY, Boolean.valueOf(false), Boolean.valueOf(true)); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#toFront() */ public void toFront() { if (model.getState() == DISCARDED) return; controller.toFront(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setIconImage(BufferedImage) */ public void setIconImage(BufferedImage thumbnail) { if (model.getState() == DISCARDED) return; //view.setIconImage(thumbnail); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setRndImage(Object) */ public void setRndImage(Object rndImage) { if (model.getState() == DISCARDED) return; model.setRenderedImage(rndImage); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#hasROIToSave() */ public boolean hasROIToSave() { if (model.getState() == DISCARDED) return false; return model.hasROIToSave(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setServerROI(Collection) */ public void setServerROI(Collection result) { if (model.getState() != LOADING_ROI) throw new IllegalArgumentException("The method can only " + "be invoked in the LOADING_ROI state."); List<DataObject> nodes = null; try { if (result != null) { //some ROI previously saved. nodes = model.setServerROI(result); } } catch (Exception e) { String s = "Cannot convert server ROI into UI objects:"; MeasurementAgent.getRegistry().getLogger().error(this, s+e); } //bring up the UI. view.layoutUI(); view.updateDrawingArea(); fireStateChange(); //Now we are ready to go. We can post an event to add component to //Viewer postEvent(MeasurementToolLoaded.ADD); if (CollectionUtils.isNotEmpty(nodes)) { model.fireLoadROIAnnotations(nodes); } } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#isHCSData() */ public boolean isHCSData() { return model.isHCSData(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#getViewTitle() */ public String getViewTitle() { return model.getImageTitle(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setLoadingFromServerClient(Collection) */ public void setLoadingFromServerClient(Collection result) { if (model.getState() != LOADING_ROI) throw new IllegalArgumentException("The method can only " + "be invoked in the LOADING_ROI state."); List<DataObject> nodes = null; try { boolean hasResult = false; if (result != null) { Iterator<ROIResult> i = result.iterator(); ROIResult roiResult; if (i.hasNext()) { roiResult = i.next(); if (CollectionUtils.isNotEmpty(roiResult.getROIs())) hasResult = true; } } if (hasResult) { //some ROI previously saved. //result.ge nodes = model.setServerROI(result); } else { model.fireROILoading(null); return; } } catch (Exception e) { String s = "Cannot convert server ROI into UI objects:"; MeasurementAgent.getRegistry().getLogger().error(this, s+e); UserNotifier un = MeasurementAgent.getRegistry().getUserNotifier(); un.notifyInfo("Load ROI", "Cannot display the ROI."); } view.refreshToolBar(); view.rebuildManagerTable(); view.refreshResultsTable(); view.updateDrawingArea(); view.setReadyStatus(); fireStateChange(); //Now we are ready to go. We can post an event to add component to //Viewer postEvent(MeasurementToolLoaded.ADD); if (CollectionUtils.isNotEmpty(nodes)) { model.fireLoadROIAnnotations(nodes); } } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setUpdateROIComponent(Collection) */ public void setUpdateROIComponent(Collection result) { Registry reg = MeasurementAgent.getRegistry(); UserNotifier un = reg.getUserNotifier(); try { model.removeAllROI(); view.rebuildManagerTable(); view.clearInspector(); view.refreshResultsTable(); view.updateDrawingArea(); } catch (NoSuchROIException e) { reg.getLogger().error(this, "Cannot save the ROI "+e.getMessage()); un.notifyInfo("Save ROI", "Cannot save ROI " + "for "+model.getImageID()); } model.fireLoadROIServerOrClient(false); } @Override public long getImageID() { return model.getImageID(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#canAnnotate() */ public boolean canAnnotate() { if (model.getState() == DISCARDED) return false; //Check if current user can write in object ExperimenterData exp = (ExperimenterData) MeasurementAgent.getUserDetails(); long id = exp.getId(); Object ref = model.getRefObject(); boolean b = EditorUtil.isUserOwner(ref, id); if (b) return b; if (ref instanceof DataObject) { return ((DataObject) ref).canAnnotate(); } return false; } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#canDelete() */ public boolean canDelete() { if (model.getState() == DISCARDED) return false; //Check if current user can write in object ExperimenterData exp = (ExperimenterData) MeasurementAgent.getUserDetails(); long id = exp.getId(); Object ref = model.getRefObject(); boolean b = EditorUtil.isUserOwner(ref, id); if (b) return b; if (ref instanceof DataObject) { return ((DataObject) ref).canDelete(); } return false; } /** * Overridden to return the name of the instance to save. * @see #toString() */ public String toString() { return "ROI for: "+EditorUtil.truncate(model.getImageName()); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#deleteAllROIs() */ public void deleteAllROIs(int level) { if (!canDelete()) return; List<ROIData> list; if (model.isMember()) level = MeasurementViewer.ME; list = model.getROIData(level); if (list.size() == 0) return; List<DeletableObject> l = new ArrayList<DeletableObject>(); Iterator<ROIData> i = list.iterator(); ROIData roi; SecurityContext ctx = model.getSecurityContext(); DeletableObject d; while (i.hasNext()) { roi = i.next(); if (roi.getId() > 0) { d = new DeletableObject(roi); d.setSecurityContext(ctx); l.add(d); } } //if (l.size() == 0) return; //clear view. and table. ExperimenterData exp = (ExperimenterData) MeasurementAgent.getUserDetails(); try { List<ROIFigure> figures = model.removeAllROI(exp.getId(), level); if (figures != null) { //clear all tables. view.deleteROIs(figures); model.getROIComponent().reset(); } } catch (Exception e) { LogMessage msg = new LogMessage(); msg.print("Delete ROI"); msg.print(e); MeasurementAgent.getRegistry().getLogger().error(this, msg); } model.deleteAllROIs(l); fireStateChange(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#hasROIToDelete() */ public boolean hasROIToDelete() { if (model.getState() == DISCARDED) return false; return model.hasROIToDelete(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#isMember() */ public boolean isMember() { return model.isMember(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#isMember() */ public void onUpdatedChannels(List<ChannelData> channels) { if (model.getState() == DISCARDED) return; model.setChannelData(channels); view.displayAnalysisResults(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#exportGraph() */ public void exportGraph() { view.exportGraph(); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setROIAnnotations(Map) */ public void setROIAnnotations(Map<DataObject, StructuredDataResults> result) { if (model.getState() == DISCARDED || result == null || result.size() == 0) return; //Update the UI elements Collection<ROIFigure> figures = model.getAllFigures(); if (CollectionUtils.isEmpty(figures)) return; Iterator<ROIFigure> i = figures.iterator(); ROIFigure f; ShapeData shape; Map<Long, StructuredDataResults> r = convertMap(result, null); if (r == null) return; while (i.hasNext()) { f = i.next(); shape = f.getROIShape().getData(); if (shape != null) { f.setAttribute(AnnotationKeys.TAG, r.get(shape.getId())); } } Collection<Figure> figs = view.getSelectedFiguresFromTables(); if (CollectionUtils.isNotEmpty(figs)) { List<ROIShape> shapes = new ArrayList<ROIShape>(); Iterator<Figure> j = figs.iterator(); while (j.hasNext()) { f = (ROIFigure) j.next(); shapes.add(f.getROIShape()); } view.displayAnnotations(shapes); } } /** * Converts the results map. * * @param result The map to handle * @param type The type of object to look for or <code>null</code>. * @return See above. */ private Map<Long, StructuredDataResults> convertMap( Map<DataObject, StructuredDataResults> result, Class<?> type) { Map<Long, StructuredDataResults> r = new HashMap<Long, StructuredDataResults>(); Entry<DataObject, StructuredDataResults> e; Iterator<Entry<DataObject, StructuredDataResults>> i = result.entrySet().iterator(); while (i.hasNext()) { e = i.next(); if (type == null) { r.put(e.getKey().getId(), e.getValue()); } else { if (e.getKey().getClass().equals(type)) { r.put(e.getKey().getId(), e.getValue()); } } } return r; } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#setExistingTags(Collection) */ public void setExistingTags(Collection tags) { model.setExistingTags(tags); //Display the UI. Collection<Figure> shapes = model.getSelectedFigures(); Iterator<Figure> i = shapes.iterator(); ROIFigure shape; StructuredDataResults data; List<Object> l = new ArrayList<Object>(); List<Long> ids = new ArrayList<Long>(); boolean valid = tags != null && CollectionUtils.isNotEmpty(tags); TagAnnotationData d; while (i.hasNext()) { shape = (ROIFigure) i.next(); data = (StructuredDataResults) shape.getAttribute(AnnotationKeys.TAG); if (data != null && CollectionUtils.isNotEmpty(data.getTags())) { if (valid) { Iterator<TagAnnotationData> j = data.getTags().iterator(); while (j.hasNext()) { d = j.next(); ids.add(d.getId()); l.add(d); } } else { l.addAll(data.getTags()); } } } //if tags not empty. List<Object> available = new ArrayList<Object>(); if (valid) { Iterator j = tags.iterator(); while (j.hasNext()) { AnnotationData object = (AnnotationData) j.next(); if (!ids.contains(object.getId())) { available.add(object); } } } //Bring up the selection Wizard SelectionWizard wizard = new SelectionWizard( view, available, l, TagAnnotationData.class, true, model.getCurrentUser()); wizard.addPropertyChangeListener(controller); UIUtilities.centerAndShow(wizard); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#loadTags() */ public void loadTags() { Collection tags = model.getExistingTags(); if (tags == null) { model.fireExistingTagsLoading(); } else { setExistingTags(tags); } } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#tagSelectedFigures() */ public void tagSelectedFigures(List<AnnotationData> tags) { Collection<Figure> figures = view.getSelectedFiguresFromTables(); if (CollectionUtils.isEmpty(figures)) { return; } List<ROIShape> shapes = new ArrayList<ROIShape>(); Iterator<Figure> kk = figures.iterator(); ROIFigure fig; while (kk.hasNext()) { fig = (ROIFigure) kk.next(); shapes.add(fig.getROIShape()); } if (CollectionUtils.isEmpty(shapes)) return; Multimap<Long, AnnotationData> m = ArrayListMultimap.create(); Iterator<AnnotationData> j = tags.iterator(); AnnotationData an; while (j.hasNext()) { an = j.next(); m.put(an.getId(), an); } Iterator<ROIShape> i = shapes.iterator(); ROIShape shape; StructuredDataResults data; List<DataObject> objects = new ArrayList<DataObject>(); ShapeData d; Map<Long, AnnotationData> mo = new HashMap<Long, AnnotationData>(); while (i.hasNext()) { shape = i.next(); d = shape.getData(); if (d != null && d.getId() > 0) { objects.add(d); data = (StructuredDataResults) shape.getFigure().getAttribute(AnnotationKeys.TAG); if (data != null && CollectionUtils.isNotEmpty(data.getTags())) { Collection<TagAnnotationData> t = data.getTags(); Iterator<TagAnnotationData> tt = t.iterator(); while (tt.hasNext()) { TagAnnotationData tag = tt.next(); if (!mo.containsKey(tag.getId())) { mo.put(tag.getId(), tag); } } } } } if (objects.isEmpty()) { UserNotifier un = MeasurementAgent.getRegistry().getUserNotifier(); un.notifyInfo("ROI Annotations", "You must save the ROI before annotating it."); return; } //Now we prepare the list of annotations to add or remove List<AnnotationData> toAdd = new ArrayList<AnnotationData>(); List<Object> toRemove = new ArrayList<Object>(); if (CollectionUtils.isNotEmpty(m.get(-1L))) { toAdd.addAll(m.removeAll(-1L)); } Iterator<Entry<Long, AnnotationData>> k = m.entries().iterator(); Entry<Long, AnnotationData> e; while (k.hasNext()) { e = k.next(); Long id = e.getKey(); if (!mo.containsKey(id)) { toAdd.add(e.getValue()); } } k = mo.entrySet().iterator(); while (k.hasNext()) { e = k.next(); Long id = e.getKey(); if (!m.containsKey(id)) { toRemove.add(e.getValue()); } } model.fireAnnotationSaving(objects, toAdd, toRemove); } /** * Implemented as specified by the {@link MeasurementViewer} interface. * @see MeasurementViewer#onAnnotationSaved() */ public void onAnnotationSaved() { //Load the annotation for the selected shapes. //Display the UI. Collection<ROIFigure> figures = model.getAllFigures(); if (CollectionUtils.isEmpty(figures)) { return; } List<ROIShape> shapes = new ArrayList<ROIShape>(); Iterator<ROIFigure> kk = figures.iterator(); ROIFigure fig; while (kk.hasNext()) { fig = kk.next(); shapes.add(fig.getROIShape()); } if (CollectionUtils.isEmpty(shapes)) return; Iterator<ROIShape> i = shapes.iterator(); ROIShape shape; List<DataObject> nodes = new ArrayList<DataObject>(); while (i.hasNext()) { shape = (ROIShape) i.next(); nodes.add(shape.getData()); } model.fireLoadROIAnnotations(nodes); } }