/*
*------------------------------------------------------------------------------
* 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.Rectangle;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import org.openmicroscopy.shoola.agents.events.iviewer.MeasurementTool;
import org.openmicroscopy.shoola.agents.events.measurement.ROIEvent;
import org.openmicroscopy.shoola.agents.measurement.MeasurementAgent;
import org.openmicroscopy.shoola.agents.measurement.actions.ActivationAction;
import omero.gateway.SecurityContext;
import org.openmicroscopy.shoola.env.ui.TaskBar;
import omero.gateway.model.ChannelData;
import omero.gateway.model.PixelsData;
/**
* Factory to create {@link MeasurementViewer} components.
* This class keeps track of all {@link MeasurementViewer} instances that have
* been created and are not yet {@link MeasurementViewer#DISCARDED discarded}.
* A new component is only created if none of the <i>tracked</i> ones is already
* displaying the given hierarchy. Otherwise, the existing component is
* recycled.
*
* @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
*/
public class MeasurementViewerFactory
implements ChangeListener
{
/** The name of the windows menu. */
private static final String MENU_NAME = "ROI Tool";
/** The sole instance. */
private static final MeasurementViewerFactory
singleton = new MeasurementViewerFactory();
/**
* Returns the <code>window</code> menu.
*
* @return See above.
*/
static JMenu getWindowMenu() { return singleton.windowMenu; }
/** Attaches the {@link #windowMenu} to the <code>TaskBar</code>. */
static void attachWindowMenuToTaskBar()
{
if (singleton.isAttached) return;
TaskBar tb = MeasurementAgent.getRegistry().getTaskBar();
tb.addToMenu(TaskBar.WINDOW_MENU, singleton.windowMenu);
singleton.isAttached = true;
}
/**
* Adds all the {@link MeasurementViewer} components that this factory is
* currently tracking to the passed menu.
*
* @param menu The menu to add the components to.
*/
static void register(JMenu menu)
{
if (menu == null) return;
Iterator<MeasurementViewer> i = singleton.viewers.iterator();
menu.removeAll();
while (i.hasNext())
menu.add(new JMenuItem(new ActivationAction(i.next())));
}
/**
* Returns a viewer to display the image corresponding to the specified id.
* Recycles or creates a viewer.
*
* @param ctx The security context.
* @param pixels The pixels set the measurement tool is for.
* @param imageID The id of the image.
* @param name The name of the image.
* @param bounds The bounds of the component invoking the
* {@link MeasurementViewer}.
* @param z The selected z-section.
* @param t The selected time-point.
* @param magnification The image's magnification factor.
* @param activeChannels Collection of active channels.
* @param channelsData The channels metadata.
* @return See above.
*/
public static MeasurementViewer getViewer(SecurityContext ctx,
PixelsData pixels, long imageID, String name, Rectangle bounds,
int z, int t, double magnification,
Map activeChannels, List<ChannelData> channelsData)
{
MeasurementViewerModel model = new MeasurementViewerModel(ctx, imageID,
pixels, name, bounds, channelsData);
model.setPlane(z, t);
model.setMagnification(magnification);
model.setActiveChannels(activeChannels);
return singleton.createROIViewer(model);
}
/**
* Returns a viewer or <code>null</code> if not previously created.
*
* @param ctx The security context.
* @param pixelsID The id of the pixels set.
* @return See above.
*/
public static MeasurementViewer getViewer(SecurityContext ctx,
long pixelsID)
{
Iterator<MeasurementViewer> v = singleton.viewers.iterator();
MeasurementViewerComponent comp;
while (v.hasNext()) {
comp = (MeasurementViewerComponent) v.next();
if (comp.getModel().getPixelsID() == pixelsID) return comp;
}
return null;
}
/**
* Returns a viewer or <code>null</code> if not previously created.
*
* @param ctx The security context.
* @param imageID The id of the image.
* @return See above.
*/
public static MeasurementViewer getViewerFromImage(SecurityContext ctx,
long imageID)
{
Iterator<MeasurementViewer> v = singleton.viewers.iterator();
MeasurementViewerComponent comp;
while (v.hasNext()) {
comp = (MeasurementViewerComponent) v.next();
if (comp.getModel().getImageID() == imageID) return comp;
}
return null;
}
/**
* Adds the passed request to the collection.
*
* @param request The request to add.
*/
public static void addRequest(MeasurementTool request)
{
if (request == null) return;
singleton.requests.add(request);
}
/**
* Returns the request, if any, identified by the pixels ID.
*
* @param pixelsID The id of the pixels set.
* @return See above.
*/
public static MeasurementTool getRequest(long pixelsID)
{
Iterator<MeasurementTool> v = singleton.requests.iterator();
MeasurementTool request;
PixelsData pixels;
while (v.hasNext()) {
request = (MeasurementTool) v.next();
pixels = request.getPixels();
if (pixels != null) {
if (pixels.getId() == pixelsID) return request;
}
}
return null;
}
/**
* Returns the instances to save.
*
* @return See above.
*/
public static List<Object> getInstancesToSave()
{
if (singleton.viewers.size() == 0) return null;
List<Object> instances = new ArrayList<Object>();
Iterator<MeasurementViewer> i = singleton.viewers.iterator();
MeasurementViewerComponent comp;
while (i.hasNext()) {
comp = (MeasurementViewerComponent) i.next();
if (comp.hasROIToSave())
instances.add(comp);
}
return instances;
}
/**
* Saves the passed instances and discards them.
*
* @param instances The instances to save.
*/
public static void saveInstances(List<Object> instances)
{
if (instances != null) {
Iterator<Object> i = instances.iterator();
Object o;
MeasurementViewerComponent comp;
while (i.hasNext()) {
o = i.next();
if (o instanceof MeasurementViewerComponent) {
comp = (MeasurementViewerComponent) o;
comp.saveAndDiscard();
}
}
}
}
/**
* Notifies the model that the user's group has successfully be modified
* if the passed value is <code>true</code>, unsuccessfully
* if <code>false</code>.
*
* @param success Pass <code>true</code> if successful, <code>false</code>
* otherwise.
*/
public static void onGroupSwitched(boolean success)
{
if (!success) return;
singleton.clear();
}
/**
* Notifies the model that the ROIs have been deleted
*
* @param imageID The identifier of the image.
*/
public static void onROIDeleted(long imageID)
{
MeasurementAgent.getRegistry().getEventBus().post(new ROIEvent(imageID));
if (singleton.viewers.size() == 0) return;
Iterator<MeasurementViewer> i = singleton.viewers.iterator();
MeasurementViewerComponent comp;
while (i.hasNext()) {
comp = (MeasurementViewerComponent) i.next();
comp.onROIDeleted(imageID);
if (comp.hasROIToSave())
comp.saveROIToServer(false);
}
}
/** All the tracked components. */
private Set<MeasurementViewer> viewers;
/** All the tracked requests. */
private Set<MeasurementTool> requests;
/** The windows menu. */
private JMenu windowMenu;
/**
* Indicates if the {@link #windowMenu} is attached to the
* <code>TaskBar</code>.
*/
private boolean isAttached;
/** Creates a new instance. */
private MeasurementViewerFactory()
{
viewers = new HashSet<MeasurementViewer>();
requests = new HashSet<MeasurementTool>();
isAttached = false;
windowMenu = new JMenu(MENU_NAME);
}
/** Discards the tracked viewers. */
private void clear()
{
if (viewers.size() == 0) return;
Iterator<MeasurementViewer> i = viewers.iterator();
MeasurementViewerComponent comp;
while (i.hasNext()) {
comp = (MeasurementViewerComponent) i.next();
comp.removeChangeListener(this);
comp.discard();
}
viewers.clear();
requests.clear();
handleViewerDiscarded();
}
/**
* Creates or recycles a viewer component for the specified
* <code>model</code>.
*
* @param model The component's Model.
* @return A {@link MeasurementViewer} for the specified <code>model</code>.
*/
private MeasurementViewer createROIViewer(MeasurementViewerModel model)
{
Iterator<MeasurementViewer> v = viewers.iterator();
MeasurementViewerComponent comp;
while (v.hasNext()) {
comp = (MeasurementViewerComponent) v.next();
if (model.isSameDisplay(comp.getModel())) return comp;
}
comp = new MeasurementViewerComponent(model);
comp.initialize();
comp.addChangeListener(this);
viewers.add(comp);
return comp;
}
/**
* Checks the list of opened viewers before removing the entry from the
* menu.
*/
private void handleViewerDiscarded()
{
if (!singleton.isAttached) return;
if (singleton.viewers.size() != 0) return;
TaskBar tb = MeasurementAgent.getRegistry().getTaskBar();
tb.removeFromMenu(TaskBar.WINDOW_MENU, singleton.windowMenu);
singleton.isAttached = false;
}
/**
* Removes a viewer from the {@link #viewers} set when it is
* {@link MeasurementViewer#DISCARDED discarded}.
* @see ChangeListener#stateChanged(ChangeEvent)
*/
public void stateChanged(ChangeEvent e)
{
MeasurementViewerComponent comp =
(MeasurementViewerComponent) e.getSource();
if (comp.getState() == MeasurementViewer.DISCARDED) {
viewers.remove(comp);
handleViewerDiscarded();
}
}
}