/*
 * org.openmicroscopy.shoola.agents.iviewer.view.ImViewerComponent
 *
 *------------------------------------------------------------------------------
 *  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.imviewer.view;


//Java imports
import java.awt.Color;
import java.awt.Component;
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

//Third-party libraries
import com.sun.opengl.util.texture.TextureData;

//Application-internal dependencies
import org.openmicroscopy.shoola.agents.events.iviewer.ChannelSelection;
import org.openmicroscopy.shoola.agents.events.iviewer.ImageRendered;
import org.openmicroscopy.shoola.agents.events.iviewer.MeasurePlane;
import org.openmicroscopy.shoola.agents.events.iviewer.MeasurementTool;
import org.openmicroscopy.shoola.agents.events.iviewer.RndSettingsCopied;
import org.openmicroscopy.shoola.agents.events.iviewer.SaveRelatedData;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewImage;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewImageObject;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewerCreated;
import org.openmicroscopy.shoola.agents.events.iviewer.ViewerState;
import org.openmicroscopy.shoola.agents.events.treeviewer.NodeToRefreshEvent;
import org.openmicroscopy.shoola.agents.imviewer.IconManager;
import org.openmicroscopy.shoola.agents.imviewer.ImViewerAgent;
import org.openmicroscopy.shoola.agents.imviewer.actions.ColorModelAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.PlayMovieAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.UnitBarSizeAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.ZoomAction;
import org.openmicroscopy.shoola.agents.imviewer.util.PreferencesDialog;
import org.openmicroscopy.shoola.agents.imviewer.util.proj.ProjSavingDialog;
import org.openmicroscopy.shoola.agents.imviewer.util.proj.ProjectionRef;
import org.openmicroscopy.shoola.agents.imviewer.util.UnitBarSizeDialog;
import org.openmicroscopy.shoola.agents.imviewer.util.player.MoviePlayerDialog;
import org.openmicroscopy.shoola.agents.metadata.rnd.Renderer;
import org.openmicroscopy.shoola.agents.util.EditorUtil;
import org.openmicroscopy.shoola.agents.util.flim.FLIMResultsDialog;
import org.openmicroscopy.shoola.env.config.Registry;
import org.openmicroscopy.shoola.env.data.model.ProjectionParam;
import org.openmicroscopy.shoola.env.data.util.SecurityContext;
import org.openmicroscopy.shoola.env.event.EventBus;
import org.openmicroscopy.shoola.env.log.LogMessage;
import org.openmicroscopy.shoola.env.log.Logger;
import org.openmicroscopy.shoola.env.rnd.RenderingControl;
import org.openmicroscopy.shoola.env.rnd.RndProxyDef;
import org.openmicroscopy.shoola.env.rnd.data.Tile;
import org.openmicroscopy.shoola.env.ui.SaveEventBox;
import org.openmicroscopy.shoola.env.ui.UserNotifier;
import org.openmicroscopy.shoola.util.image.geom.Factory;
import org.openmicroscopy.shoola.util.ui.MessageBox;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.component.AbstractComponent;
import org.openmicroscopy.shoola.util.ui.drawingtools.canvas.DrawingCanvasView;
import pojos.ChannelData;
import pojos.DataObject;
import pojos.ExperimenterData;
import pojos.FileAnnotationData;
import pojos.ImageData;


/** 
 * Implements the {@link ImViewer} interface to provide the functionality
 * required of the hierarchy 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.imviewer.view.ImViewerModel
 * @see org.openmicroscopy.shoola.agents.imviewer.view.ImViewerUI
 * @see org.openmicroscopy.shoola.agents.imviewer.view.ImViewerControl
 * 
 * @author  Jean-Marie Burel &nbsp;&nbsp;&nbsp;&nbsp;
 * 				<a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a>
 * @author	Andrea Falconi &nbsp;&nbsp;&nbsp;&nbsp;
 * 			<a href="mailto:a.falconi@dundee.ac.uk">a.falconi@dundee.ac.uk</a>
 * @author	Donald MacDonald &nbsp;&nbsp;&nbsp;&nbsp;
 * <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 OME2.2
 */
class ImViewerComponent
	extends AbstractComponent
	implements ImViewer
{

	/** The message if rendering setting to save. */
	static final String						RND = "The last rendering settings";
	
	/** The message if rendering setting to annotation. */
	static final String						ANNOTATION = "The annotations";
	
	/** The Model sub-component. */
	private ImViewerModel       			model;

	/** The Control sub-component. */
	private ImViewerControl     			controller;

	/** The View sub-component. */
	private ImViewerUI          			view;

	/** Collection of events to display. */
	private Map<String, SaveRelatedData>	events;
	
	/** Flag indicating that a new z-section or timepoint is selected. */
	private boolean							newPlane;

	/** 
	 * Flag indicating that the rendering settings have been saved
	 * before copying.
	 */
	private boolean							saveBeforeCopy;

    /** The color model used. */
    private String							colorModel;
    
    /** The projection dialog. */
    private ProjSavingDialog 				projDialog;
    
    /** The list of component added to the main viewer. */
    private List<JComponent>				layers;
    
    /** The color changes preview.*/
    private Map<Integer, Color>				colorChanges;
    
    /** Flag indicating that it was not possible to save the settings.*/
    private boolean failureToSave;
    
	/**
	 * Creates and returns an image including the ROI
	 * 
	 * @param image The image to handle.
	 * @return See above.
	 */
	private BufferedImage createImageWithROI(BufferedImage image)
	{
		Iterator<JComponent> i = layers.iterator();
		JComponent c;
		BufferedImage img = Factory.copyBufferedImage(image);
		DrawingCanvasView canvas;
		while (i.hasNext()) {
			c = i.next();
			if (c instanceof DrawingCanvasView) {
				canvas = (DrawingCanvasView) c;
				canvas.print(img.getGraphics());
			}
		}
		return img;
	}
	
	/** 
	 * Brings up the dialog used to set the parameters required for the
	 * projection.
	 */
	private void showProjectionDialog()
	{
		if (projDialog == null) {
			projDialog = new ProjSavingDialog(view, model.getParent(),
					model.getGrandParent());
			projDialog.initialize(view.getProjectionType(), model.getRealT(),
					model.getPixelsType(), model.getImageName(), 
					model.getContainers(), model.getMaxZ()+1, 
					view.getProjectionStartZ()+1, view.getProjectionEndZ()+1);
			projDialog.addPropertyChangeListener(controller);
			projDialog.pack();
			Dimension minimumSize = new Dimension(projDialog.getWidth(),
					projDialog.getHeight());
			projDialog.setMinimumSize(minimumSize);
		} else {
			projDialog.setProjectionInterval(view.getProjectionStartZ()+1,
					view.getProjectionEndZ()+1);
		}
		UIUtilities.centerAndShow(projDialog);
	}

	/**
	 * Posts a {@link MeasurePlane} event to indicate that a new plane is
	 * rendered or a new magnification factor has been selected.
	 */
	private void postMeasurePlane()
	{
		EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		double f = model.getZoomFactor()*model.getOriginalRatio();
		if (model.isBigImage()) {
			f = view.getBigImageMagnificationFactor();
		}
		MeasurePlane event = new MeasurePlane(model.getPixelsID(), 
				model.getDefaultZ(), model.getDefaultT(), f);
		if (model.isBigImage()) {
			event.setSize(model.getTiledImageSizeX(),
					model.getTiledImageSizeY());
		}
		bus.post(event);
	}

	/**
	 * Posts an {@link ViewerState} event to indicate that the frame state
	 * of this component has changed.
	 * 
	 * @param index One of the constants defined by {@link ViewerState}.
	 */
	private void postViewerState(int index)
	{
		EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		ViewerState event = new ViewerState(model.getPixelsID(), index);
		bus.post(event);
	}

	/**
	 * Posts an {@link ChannelSelection} event to indicate that the 
	 * a new channel is selected or unselected; or that a channel is mapped
	 * to a new color.
	 * 
	 * @param index One of the constants defined by {@link ChannelSelection}.
	 */
	private void postActiveChannelSelection(int index)
	{
		EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		ChannelSelection event = new ChannelSelection(model.getPixelsID(), 
				model.getActiveChannelsColorMap(), index);
		bus.post(event);
		view.setPlaneInfoStatus();
	}
	
	/** Saves the plane quietly i.e. no question asked to the user. */
	private void savePlane()
	{
		if (model.isOriginalPlane()) return;
		try {
			saveRndSettings(true);
		} catch (Exception e) {
			LogMessage logMsg = new LogMessage();
			logMsg.println("Cannot save rendering settings. ");
			logMsg.print(e);
			ImViewerAgent.getRegistry().getLogger().error(this, logMsg);
		}
	}
	
	/** 
	 * Displays message before closing the viewer. 
	 * Returns <code>true</code> if we need to close the viewer,
	 * <code>false</code> otherwise.
	 * 
	 * @param notifyUser Pass <code>true</code> to notify the user, 
	 * 					<code>false</code> otherwise.
	 * @return See above.
	 */
	private boolean saveOnClose(boolean notifyUser)
	{
		if (!canAnnotate()) return true;
		if (failureToSave) return true;
		if (!notifyUser) {
			saveRndSettings(true);
			return true;
		}
		boolean showBox = false;
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
		JCheckBox rndBox = null;
		if (!model.isOriginalSettings()) {
			rndBox = new JCheckBox(RND);
			rndBox.setSelected(true);
			p.add(rndBox);
			showBox = true;
		}
		JCheckBox annotationBox = null;
		if (model.hasMetadataToSave()) {
			annotationBox = new JCheckBox(ANNOTATION);
			annotationBox.setSelected(true);
			p.add(annotationBox);
			showBox = true;
		}
		
		List<SaveEventBox> boxes = null;
		SaveEventBox box;
		Iterator j;
		if (events != null) {
			boxes = new ArrayList<SaveEventBox>(events.size());
			j = events.keySet().iterator();
			SaveRelatedData value;
			while (j.hasNext()) {
				value = events.get(j.next());
				if (value.isToSave()) {
					showBox = true;
					box = new SaveEventBox(value);
					boxes.add(box);
					p.add(box);
				}
			}
		}
		if (!showBox) {
			if (saveBeforeCopy) {
				try {
					model.saveRndSettings(false);
				} catch (Exception e) {
					LogMessage logMsg = new LogMessage();
					logMsg.println("Cannot save rendering settings. ");
					logMsg.print(e);
					ImViewerAgent.getRegistry().getLogger().error(this, logMsg);
				}
			}
			savePlane();
			return true;
		}
		MessageBox msg = new MessageBox(view, "Save Data", 
		"Before closing the viewer, do you want to save: ");
		msg.addCancelButton();
		msg.addBodyComponent(p);
		
		int option = msg.centerMsgBox();
		if (option == MessageBox.YES_OPTION) {
			if (saveBeforeCopy) {
				try {
					model.saveRndSettings(false);
				} catch (Exception e) {
					LogMessage logMsg = new LogMessage();
					logMsg.println("Cannot save rendering settings. ");
					logMsg.print(e);
					ImViewerAgent.getRegistry().getLogger().error(this, logMsg);
				}
			}
			if (rndBox != null && rndBox.isSelected()) {
				try {
					saveRndSettings(true);
				} catch (Exception e) {
					LogMessage logMsg = new LogMessage();
					logMsg.println("Cannot save rendering settings. ");
					logMsg.print(e);
					ImViewerAgent.getRegistry().getLogger().error(this, logMsg);
				}
			}
			if (rndBox == null) {
				savePlane();
			}
			if (annotationBox != null && annotationBox.isSelected())
				model.saveMetadata();
			if (boxes != null) {
				j = boxes.iterator();
				EventBus bus = ImViewerAgent.getRegistry().getEventBus();
				SaveRelatedData event;
				while (j.hasNext()) {
					box = (SaveEventBox) j.next();
					if (box.isSelected()) {
						event = (SaveRelatedData) box.getEvent();
						if (event.isToSave()) {
							bus.post(event.getSaveEvent());
						}
					}
				}
			}
			return true;
		} else if (option == MessageBox.CANCEL) {
			return false;
		} else if (option == MessageBox.NO_OPTION) {
			if (saveBeforeCopy) {
				try {
					model.saveRndSettings(false);
				} catch (Exception e) {
					LogMessage logMsg = new LogMessage();
					logMsg.println("Cannot save rendering settings. ");
					logMsg.print(e);
					ImViewerAgent.getRegistry().getLogger().error(this, logMsg);
				}
			}
			model.resetMappingSettings(model.getOriginalDef());
		}
		return true;
	}

	/**
	 * Notifies that the projected image has been created and asks if the
	 * user wants to launch a viewer with the projected image.
	 * 
	 * @param message 	The message to display.
	 * @param image		The projected image.
	 */
	private void notifyProjection(String message, ImageData image)
	{
		model.setState(READY);
		String text = message;
		text += "\n";
		text += "Do you want to launch a viewer for the projected image?";
		
		MessageBox msg = new MessageBox(view, "Projection", text);
		int option = msg.centerMsgBox();
		if (option == MessageBox.YES_OPTION) {
			EventBus bus = ImViewerAgent.getRegistry().getEventBus();
			bus.post(new ViewImage(model.getSecurityContext(),
					new ViewImageObject(image), null));
		}
	}
	
	/** Fires an asynchronous call to preview the projection. */
	private void previewProjection()
	{
		switch (model.getState()) {
			case DISCARDED:
			case PROJECTING:
			case PROJECTION_PREVIEW: 
				return;
		}
		model.fireRenderProjected(view.getProjectionStartZ(), 
				view.getProjectionEndZ(), view.getProjectionStepping(), 
				view.getProjectionType());
		fireStateChange();
	}
	
	/** 
	 * Returns <code>true</code> if it is the same projection parameters,
	 * <code>false</code> otherwise.
	 * 
	 * @return See above.
	 */
	private boolean isSameProjectionParam()
	{
		ProjectionParam ref = model.getLastProjRef();
		if (ref == null) return true;
		if (ref.getStartZ() != view.getProjectionStartZ()) return false;
		if (ref.getEndZ() != view.getProjectionEndZ()) return false;
		if (ref.getAlgorithm() != view.getProjectionType()) return false;
		if (ref.getStepping() != view.getProjectionStepping()) return false;
		return true;
	}
	
	/**
	 * Posts an event to bring up the measurement tool.
	 * 
	 * @param measurements The measurements to load.
	 */
	private void postMeasurementEvent(List<FileAnnotationData> measurements)
	{
		EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		double f = 
			model.getZoomFactor()*model.getOriginalRatio();
		if (model.isBigImage()) f = view.getBigImageMagnificationFactor();
		MeasurementTool request = new MeasurementTool(
				model.getSecurityContext(), model.getImageID(),
				model.getPixelsData(), model.getImageName(),
				model.getDefaultZ(), model.getDefaultT(),
				model.getActiveChannelsColorMap(),f,
				view.getBounds(), model.getChannelData());
		if (model.isBigImage()) {
			request.setSize(model.getTiledImageSizeX(),
					model.getTiledImageSizeY());
		}
		request.setThumbnail(model.getImageIcon());
		request.setRenderedImage(model.getBrowser().getRenderedImage());
		request.setMeasurements(measurements);
		request.setHCSData(model.isHCSImage());
		request.setBigImage(model.isBigImage());
		bus.post(request);
		int tabbedIndex = model.getTabbedIndex();
		if (tabbedIndex != ImViewer.VIEW_INDEX) {
			view.selectTabbedPane(ImViewer.VIEW_INDEX);
			renderXYPlane();
		}
	}

	/**
	 * Posts an event to indicate to embed the image viewer.
	 * 
	 * @param toAdd  Pass <code>true</code> to embed it, <code>false</code>
	 * 				 to remove it.
	 * @param detach Pass <code>true</code> to detach it, <code>false</code>
	 * 				 otherwise.
	 */
	private void postViewerCreated(boolean toAdd, boolean detach)
	{
		JComponent c = null;
		if (toAdd) {
			showView(RENDERER_INDEX);
			c = view.asComponent();
		} 
		ViewerCreated evt = new ViewerCreated(c, 
				model.getMetadataViewer().getEditorUI(), toAdd);
		evt.setDetach(detach);
		EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		bus.post(evt);
	}
	
	/**
	 * Creates a new instance.
	 * The {@link #initialize() initialize} method should be called straight 
	 * after to complete the MVC set up.
	 * 
	 * @param model The Model sub-component. Mustn't be <code>null</code>.
	 */
	ImViewerComponent(ImViewerModel model)
	{
		if (model == null) throw new NullPointerException("No model.");
		this.model = model;
		controller = new ImViewerControl();
		view = new ImViewerUI(model.getImageTitle());
	}

	/** Links up the MVC triad. */
	void initialize()
	{
		model.initialize(this);
		controller.initialize(this, view);
		view.initialize(controller, model);
		if (model.getMetadataViewer() != null)
			model.getMetadataViewer().addPropertyChangeListener(controller);
	}

	/**
	 * Returns the Model sub-component.
	 * 
	 * @return See above.
	 */
	ImViewerModel getModel() { return model; }

	/** Sets the image to copy the rendering settings from. */
	void copyRndSettings()
	{
		view.enablePasteButton(true);
		firePropertyChange(RND_SETTINGS_PROPERTY, Boolean.FALSE, Boolean.TRUE);
	}

	/**
	 * Stored the event to display.
	 * 
	 * @param evt The event to store.
	 */
	void storeEvent(SaveRelatedData evt)
	{
		if (events == null) events = new HashMap<String, SaveRelatedData>();
 		events.put(evt.toString(), evt);
	}
	
	/**
	 * Returns a map with events to save.
	 * 
	 * @return See above.
	 */
	Map<String, SaveRelatedData> getSaveEvents() 
	{
		return events;
	}
	
	/**
	 * Returns the title associated to the viewer.
	 * 
	 * @return See above.
	 */
	String getTitle() { return view.getTitle(); }

	/** 
     * Invokes when the rendering settings has been saved using another way.
     * 
     * @param settings The save rendering settings.
     */
    void onRndSettingsSaved(RndProxyDef settings)
    {
    	if (settings == null) return;
    	model.resetOriginalSettings(settings);
    	refresh();
    	fireStateChange();
    }
    
    /**
     * Returns <code>true</code> if some settings to save, <code>false</code>
     * otherwise.
     * 
     * @return See above.
     */
	boolean hasSettingsToSave()
	{
		if (failureToSave) return false;
		return !isOriginalSettings();
	}
	
	/**
	 * Sets the display mode.
	 * 
	 * @param displayMode The value to set.
	 */
	void setDisplayMode(int displayMode)
	{
		
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#activate(RndProxyDef, long, int)
	 */
	public void activate(RndProxyDef settings, long userID, int displayMode)
	{
		model.setDisplayMode(displayMode);
		switch (model.getState()) {
			case NEW:
				model.setAlternativeSettings(settings, userID);
				/*
				if (model.isImageLoaded())
					model.fireRenderingControlLoading(model.getPixelsID());
				else model.fireImageLoading();
				*/
				if (!model.isImageLoaded()) {
					model.fireImageLoading();
				} else {
					model.setState(ImViewer.LOADING_IMAGE_DATA);
					setImageData(model.getImage());
				}
				fireStateChange();
				break;
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED state.");
			default:
				if (view != null) {
					if (model.isSeparateWindow()) {
						UIUtilities.centerOnScreen(view);
						view.toFront();
						view.requestFocusInWindow();
					} else {
						postViewerCreated(true, false);
					}
				}
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#discard()
	 */
	public void discard()
	{
		model.discard();
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#changeUserGroup(long, long)
	 */
	public void changeUserGroup(long groupID, long oldGroupID)
	{
		
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getState()
	 */
	public int getState() { return model.getState(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setStatus(String, int)
	 */
	public void setStatus(String description, int perc)
	{
		if (model.getState() == DISCARDED) return;
		view.setLeftStatus(description);
		if (perc == 100) view.setLeftStatus();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setZoomFactor(double, int)
	 */
	public void setZoomFactor(double factor, int zoomIndex)
	{
		if (factor != ZoomAction.ZOOM_FIT_FACTOR &&
			(factor > ZoomAction.MAX_ZOOM_FACTOR ||
					factor < ZoomAction.MIN_ZOOM_FACTOR))
			throw new IllegalArgumentException("The zoom factor is a value " +
					"between "+ZoomAction.MIN_ZOOM_FACTOR+" and "+
					ZoomAction.MAX_ZOOM_FACTOR);
		switch (model.getState()) {
			case NEW:
			case LOADING_IMAGE:
			case LOADING_TILES:
			case DISCARDED:
				return;
		}
		if (model.isBigImage()) {
			double ox = (double) model.getTiledImageSizeX();
			double oy = (double) model.getTiledImageSizeY();
			
			model.setSelectedResolutionLevel(zoomIndex);
			view.setZoomFactor(factor, zoomIndex);
			int w = model.getTiledImageSizeX();
			int h = model.getTiledImageSizeY();
			double nx = (double) w/ox;
			double ny = (double) h/oy;
			model.getBrowser().setComponentsSize(w, h);
			model.getBrowser().setViewLocation(nx, ny);
			postMeasurePlane();
			return;
		}
		double oldFactor = model.getZoomFactor();
		if (oldFactor == factor && factor != ZoomAction.ZOOM_FIT_FACTOR) return;
		try {
			model.setZoomFactor(factor, false);
		} catch (Exception e) {
			Logger logger = ImViewerAgent.getRegistry().getLogger();
			logger.debug(this, "Cannot zoom image. Magnification: "+factor);
			model.setZoomFactor(factor, true);
		}
		view.setZoomFactor(factor, zoomIndex);
		
		if (view.isLensVisible()) {
			switch (model.getTabbedIndex()) {
				case ImViewer.VIEW_INDEX:
				case ImViewer.PROJECTION_INDEX:
					view.setImageZoomFactor((float) model.getZoomFactor());
					view.scrollLens();
			}
		}
		controller.setPreferences();
		postMeasurePlane();
	}

	/**
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isZoomFitToWindow()
	 */
	public boolean isZoomFitToWindow() { return model.isZoomFitToWindow(); }

	/**
         * Implemented as specified by the {@link ImViewer} interface.
         * @see ImViewer#isRendererLoaded()
         */
	public boolean isRendererLoaded() {
	    return model.isRendererLoaded();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setColorModel(int)
	 */
	public void setColorModel(int key)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED, " +
						"NEW or LOADING_RENDERING_CONTROL state.");
		}
		switch (key) {
			case ColorModelAction.GREY_SCALE_MODEL:
				model.setColorModel(GREY_SCALE_MODEL, true);
				break;
			case ColorModelAction.RGB_MODEL:
				model.setColorModel(RGB_MODEL, true);
				break;
			default:
				throw new IllegalArgumentException("Color Model not supported");		
		}
		view.onColorModelChanged();
		//Remove 21/09
		//firePropertyChange(COLOR_MODEL_CHANGED_PROPERTY, 
		//		Integer.valueOf(1), Integer.valueOf(-1));
		view.setColorModel(key);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setSelectedXYPlane(int, int, int)
	 */
	public void setSelectedXYPlane(int z, int t, int bin)
	{
	    if (z < 0) z = model.getDefaultZ();
	    if (t < 0) t = model.getRealSelectedT();
	    switch (model.getState()) {
	    case NEW:
	    case DISCARDED:
	        return;
	    }
	    int defaultZ = model.getDefaultZ();
	    int defaultT = model.getRealSelectedT();
	    if (bin >= 0) { //lifetime
	        int v = model.getSelectedBin();
	        firePropertyChange(ImViewer.BIN_SELECTED_PROPERTY,
	                Integer.valueOf(v), Integer.valueOf(bin));
	        if (defaultT != t) {
                firePropertyChange(ImViewer.T_SELECTED_PROPERTY,
                        Integer.valueOf(defaultT), Integer.valueOf(t));
            }
	    } else {
	        if (defaultZ == z && defaultT == t) return;
	        if (defaultZ != z) {
	            firePropertyChange(ImViewer.Z_SELECTED_PROPERTY,
	                    Integer.valueOf(defaultZ), Integer.valueOf(z));
	        }
	        if (defaultT != t) {
	            firePropertyChange(ImViewer.T_SELECTED_PROPERTY,
	                    Integer.valueOf(defaultT), Integer.valueOf(t));
	        }
	        newPlane = true;
	    }
	    model.setSelectedXYPlane(z, t, bin);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setSelectedXYPlane(int, int, int)
	 */
	public void setSelectedRegion(int z, int t, Rectangle region)
	{
		if (z < 0) z = model.getDefaultZ();
		if (t < 0) t = model.getRealSelectedT();
		if (region == null || !model.isBigImage()) {
			setSelectedXYPlane(z, t);
			return;
		}
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				return;
		}
		int defaultZ = model.getDefaultZ();
		int defaultT = model.getRealSelectedT();
		boolean reset = false;
		if (defaultZ != z) {
			reset = true;
			firePropertyChange(ImViewer.Z_SELECTED_PROPERTY, 
					Integer.valueOf(defaultZ), Integer.valueOf(z));
		}
		if (defaultT != t) {
			reset = true;
			firePropertyChange(ImViewer.T_SELECTED_PROPERTY, 
					Integer.valueOf(defaultT), Integer.valueOf(t));
		}
		Rectangle r = model.getBrowser().getVisibleRectangle();
		double f = view.getBigImageMagnificationFactor();
		Rectangle transformRegion = new Rectangle(
				(int) (region.x*f), (int) (region.y*f), (int) (region.width*f), 
				(int) (region.height*f));
				
		if (r.contains(transformRegion)) return;
		//Now determine the view size so the ROI is displayed.
		Rectangle r2 = new Rectangle(
				transformRegion.x, transformRegion.y, r.width, r.height);
		if (reset) {
			model.fireBirdEyeViewRetrieval(true);
			model.resetTiles();
			model.getBrowser().setSelectedRegion(r2);
		} else {
			model.getBrowser().setSelectedRegion(r2);
		}
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setSelectedXYPlane(int, int)
	 */
	public void setSelectedXYPlane(int z, int t)
	{
		setSelectedXYPlane(z, t, -1);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setImage(Object)
	 */
	public void setImage(Object image)
	{
		if (model.getState() == LOADING_IMAGE_CANCELLED) return;
		if (model.getState() != LOADING_IMAGE) 
			throw new IllegalStateException("This method can only be invoked " +
			"in the LOADING_IMAGE state.");
		if (image == null) { //no need to notify.
			if (ImViewerAgent.hasOpenGLSupport())
				model.setImageAsTexture(null);
			else model.setImage(null);
			return;
		}
		if (!(image instanceof BufferedImage || 
				image instanceof TextureData)) {
			model.setImage(null);
			model.setImageAsTexture(null);
			return;
		}
		view.removeComponentListener(controller);
		if (newPlane) postMeasurePlane();
		newPlane = false;
		Object originalImage;
		if (ImViewerAgent.hasOpenGLSupport()) {
			originalImage = model.getImageAsTexture();
			model.setImageAsTexture((TextureData) image);
		} else {
			originalImage = model.getOriginalImage();
			model.setImage((BufferedImage) image);
		}
		view.handleUnitBar();
		view.setLeftStatus();
		view.setPlaneInfoStatus();
		if (originalImage == null && model.isZoomFitToWindow()) {
			controller.setZoomFactor(ZoomAction.ZOOM_FIT_TO_WINDOW);
		}
		if (model.isPlayingChannelMovie())
			model.setState(ImViewer.CHANNEL_MOVIE);
		if (!model.isPlayingMovie()) {
			//Post an event
			EventBus bus = ImViewerAgent.getRegistry().getEventBus();
			BufferedImage icon = model.getImageIcon();
			bus.post(new ImageRendered(model.getPixelsID(), icon, 
					model.getBrowser().getRenderedImage()));
			//if (icon != null) view.setIconImage(icon);
		}
			
		if (!model.isPlayingMovie() && !model.isPlayingChannelMovie()) {
			if (view.isLensVisible()) view.setLensPlaneImage();
			view.createHistoryItem(null);
		}
		view.setCursor(Cursor.getDefaultCursor());
		view.addComponentListener(controller);
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#playChannelMovie(boolean)
	 */
	public void playChannelMovie(boolean play)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED, NEW " +
						"or LOADING_RENDERING_CONTROL state.");
		}
		//if (model.getState() != READY || model.getState() != CHANNEL_MOVIE) 
			//return;
		model.playMovie(play);
		view.playChannelMovie(!play);
		if (!play) {
			displayChannelMovie();
			controller.setHistoryState(READY);
		}
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setChannelColor(int, Color, boolean)
	 */
	public void setChannelColor(int index, Color c, boolean preview)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED, " +
						"NEW or LOADING_RENDERING_CONTROL state.");
		}
		if (preview) {
			if (colorChanges == null)
				colorChanges = new HashMap<Integer, Color>();
			if (c == null) {
				c = colorChanges.get(index); //reset the color.
				colorChanges.clear();
			} else {
				if (!colorChanges.containsKey(index))
					colorChanges.put(index, model.getChannelColor(index));
			}
		} else {
			if (colorChanges != null)
				colorChanges.remove(index);
		}
		if (c == null) return;
		try {
			model.setChannelColor(index, c);
			view.setChannelColor(index, c);
			if (!model.isChannelActive(index)) {
				setChannelActive(index, true);
				view.setChannelActive(index, ImViewerUI.ALL_VIEW);
			}
			if (GREY_SCALE_MODEL.equals(model.getColorModel()))
				setColorModel(ColorModelAction.RGB_MODEL);
			//else 
			//	renderXYPlane();
		} catch (Exception e) {
			Registry reg = ImViewerAgent.getRegistry();
			LogMessage msg = new LogMessage();
			msg.println("Cannot set the color of channel "+index);
			msg.print(e);
			reg.getLogger().error(this, msg);
			reg.getUserNotifier().notifyError("Set channel color", 
					"Cannot set the color of channel "+index, e);
		}

		firePropertyChange(CHANNEL_COLOR_CHANGED_PROPERTY, -1, index);
		postActiveChannelSelection(ChannelSelection.COLOR_SELECTION);
		//if (model.isChannelActive(index)) renderXYPlane();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setChannelSelection(int, boolean)
	 */
	public void setChannelSelection(int index, boolean b)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or " +
						"NEW state.");
		}
		//depends on model
		model.setLastSettingsRef(model.getTabbedIndex());
		int uiIndex = -1;
		if (model.getColorModel().equals(GREY_SCALE_MODEL)) {
			if (model.getTabbedIndex() == ImViewer.GRID_INDEX) {
				List<Integer> l = new ArrayList<Integer>();
				List selectedChannels = view.getActiveChannelsInGrid();
				for (int i = 0; i < model.getMaxC(); i++) {
					if (i == index) {
						if (b) l.add(i);
					} else {
						if (selectedChannels.contains(i)) l.add(i);
					}
				}
				view.setChannelsSelection(l);
			} else if (model.getTabbedIndex() == ImViewer.PROJECTION_INDEX) {
				if (model.isChannelActive(index)) return;
				boolean c;
				for (int i = 0; i < model.getMaxC(); i++) {
					c = i == index;
					model.setChannelActive(i, c);  
					if (c) 
						firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
								Integer.valueOf(index-1), 
								Integer.valueOf(index));
				}
				uiIndex = ImViewerUI.PROJECTION_ONLY;
			} else {
				if (model.isChannelActive(index)) return;
				boolean c;
				for (int i = 0; i < model.getMaxC(); i++) {
					c = i == index;
					model.setChannelActive(i, c);  
					if (c) 
						firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
								Integer.valueOf(index-1), 
								Integer.valueOf(index));
				}
				uiIndex = ImViewerUI.VIEW_ONLY;
			}
		} else {
			uiIndex = ImViewerUI.ALL_VIEW;
			model.setChannelActive(index, b);
			firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
					Integer.valueOf(index-1), Integer.valueOf(index));
		}
		view.setChannelsSelection(uiIndex);
		model.setSelectedChannel(index);
		renderXYPlane();
		postActiveChannelSelection(ChannelSelection.CHANNEL_SELECTION);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#renderXYPlane()
	 */
	public void renderXYPlane()
	{
	    switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case LOADING_IMAGE:
			case DISCARDED:
			//case LOADING_BIRD_EYE_VIEW:
			case LOADING_RND:
				return;
		} 
		if (model.isBigImage()) {
			model.resetTiles();
			loadTiles(null);
			return;
		}
		boolean stop = false;
		int index = model.getTabbedIndex();
		RndProxyDef def;
		if (index == PROJECTION_INDEX) {
			def = model.getLastProjDef();
			boolean b = false;
			if (def != null) b = model.isSameSettings(def, false);
			if (b && isSameProjectionParam()) stop = true;
		} else {
			def = model.getLastMainDef();
			if (def != null) stop = model.isSameSettings(def, true);
		}
		//if (stop) return;
		
		if (index == PROJECTION_INDEX) {
			//if (stop) return;
			previewProjection();
			fireStateChange();
		} else if (index == GRID_INDEX) {
			if (GREY_SCALE_MODEL.equals(model.getColorModel())) {
				model.getBrowser().onColorModelChange();
			} else {
				model.fireImageRetrieval();
				newPlane = false;
				fireStateChange();
			}
		} else {
			//if (stop) return;
			model.fireImageRetrieval();
			newPlane = false;
			fireStateChange();
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setChannelActive(int, boolean)
	 */
	public void setChannelActive(int index, boolean b)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED, NEW or" +
				"LOADING_RENDERING_CONTROL state.");
		}
		model.setChannelActive(index, b);
		if (b)
			firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
					Integer.valueOf(index-1), Integer.valueOf(index));
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#displayChannelMovie()
	 */
	public void displayChannelMovie()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW" +
				"state.");
		}
		view.setChannelsSelection(ImViewerUI.ALL_VIEW);
		renderXYPlane();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getMaxC()
	 */
	public int getMaxC()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or " +
						"NEW state.");
		}
		return model.getMaxC();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getRealT()
	 */
	public int getRealT()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED, NEW or" +
					"LOADING_RENDERING_CONTROL state.");
		}
		return model.getRealT();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getMaxZ()
	 */
	public int getMaxZ()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED, NEW or" +
					"LOADING_RENDERING_CONTROL state.");
		}
		return model.getMaxZ();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getImageName()
	 */
	public String getImageName()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED state.");
		return model.getImageName();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getColorModel()
	 */
	public String getColorModel()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW"
						+" state.");
		}
		return model.getColorModel();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUI()
	 */
	public JFrame getUI()
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or" +
					" NEW state.");
		}
		return view;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#iconified(boolean)
	 */
	public void iconified(boolean b)
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			//throw new IllegalStateException(
			//"This method can't be invoked in the DISCARDED, NEW state.");
			return;
		}
		Boolean newValue =  Boolean.FALSE;
		Boolean oldValue = Boolean.TRUE;
		int index = ViewerState.DEICONIFIED;
		if (b) {
			newValue = Boolean.TRUE;
			oldValue = Boolean.FALSE;
			index = ViewerState.ICONIFIED;
		} 
		postViewerState(index);
		firePropertyChange(ICONIFIED_PROPERTY, oldValue, newValue);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getDefaultZ()
	 */
	public int getDefaultZ()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW"+
				" state.");
		}
		return model.getDefaultZ();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getDefaultT()
	 */
	public int getDefaultT()
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or NEW"+
			" state.");
		}
		return model.getDefaultT();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getImageComponents(String)
	 */
	public List getImageComponents(String colorModel, boolean includeROI)
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or NEW"+
			" state.");
		}
		if (model.getColorModel().equals(GREY_SCALE_MODEL)) return null;
		List l = model.getActiveChannels();
		int n = l.size();
		List<BufferedImage> images = new ArrayList<BufferedImage>(n);
		if (n == 0) return images;
		else if (n == 1) {
			images.add(getDisplayedImage(includeROI));
			return images;
		}
		Iterator i = l.iterator();
		int index;
		String oldColorModel = model.getColorModel();
		Map<Integer, BufferedImage> map = 
			new HashMap<Integer, BufferedImage>(n);
		model.setColorModel(colorModel, false);
		BufferedImage img = null, splitImage;
		while (i.hasNext()) {
			index = (Integer) i.next();
			for (int j = 0; j < model.getMaxC(); j++)
				model.setChannelActive(j, j == index); 
			splitImage = model.getSplitComponentImage();
			if (splitImage != null)
				img = Factory.magnifyImage(splitImage, 
						model.getZoomFactor(), 0);
			if (includeROI && layers != null) {
				img = createImageWithROI(img);
			}
			map.put(index, img);
		}
		model.setColorModel(oldColorModel, false);
		i = l.iterator();
		while (i.hasNext()) { //reset values.
			index = ((Integer) i.next()).intValue();
			model.setChannelActive(index, true);
		}
		List<ChannelData> channels = model.getChannelData();
		Iterator<ChannelData> k = channels.iterator();
		ChannelData channel;
		while (k.hasNext()) {
			channel = k.next();
			if (map.containsKey(channel.getIndex()))
				images.add(map.get(channel.getIndex()));
		}
		return images;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getGridImages()
	 */
	public List getGridImages()
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or NEW"+
			" state.");
		}
		view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		//if (model.getColorModel().equals(GREY_SCALE_MODEL)) return null;
		List active = model.getActiveChannels();
		int maxC = model.getMaxC();
		List<BufferedImage> images = new ArrayList<BufferedImage>(maxC);
		List<ChannelData> list = getSortedChannelData();
		Iterator<ChannelData> i = list.iterator();
		int k;
		Iterator w;
		if (model.getColorModel().equals(GREY_SCALE_MODEL)) {
			active = view.getActiveChannelsInGrid();
			//Iterator i = active.iterator();
			while (i.hasNext()) {
				k = i.next().getIndex();
				if (active.contains(k)) {
					model.setChannelActive(k, true);
					for (int j = 0; j < maxC; j++) {
						if (j != k) model.setChannelActive(j, false);
					}
					images.add(model.getSplitComponentImage());
				} else {
					images.add(null);
				}
			}

			w = active.iterator();
			while (w.hasNext()) { //reset values.
				model.setChannelActive((Integer) w.next(), true);
			}
			if (active.size() != 0) {
				model.setColorModel(RGB_MODEL, false);
				images.add(model.getSplitComponentImage());
				model.setColorModel(GREY_SCALE_MODEL, false);
			}

		} else {
			while (i.hasNext()) {
				k = i.next().getIndex();
				if (model.isChannelActive(k)) {
					for (int l = 0; l < maxC; l++) {
						model.setChannelActive(l, k == l);
					}
					images.add(model.getSplitComponentImage());
					w = active.iterator();
					while (w.hasNext()) { //reset values.
						model.setChannelActive((Integer) w.next(), true);
					}
				} else {
					images.add(null);
				}
			}
		}

		view.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		return images;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getGridImagesAsTexture()
	 */
	public Map<Integer, TextureData> getGridImagesAsTexture()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW"+
				" state.");
		}
		
		view.createGridImage(true);
		view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		//if (model.getColorModel().equals(GREY_SCALE_MODEL)) return null;
		List active = model.getActiveChannels();
		int maxC = model.getMaxC();
		Map<Integer, TextureData> 
			images = new HashMap<Integer, TextureData>(maxC);
		List<ChannelData> list = getSortedChannelData();
		Iterator<ChannelData> i = list.iterator();
		int k;
		Iterator w;
		if (model.getColorModel().equals(GREY_SCALE_MODEL)) {
			active = view.getActiveChannelsInGrid();
			//Iterator i = active.iterator();
			while (i.hasNext()) {
				k = i.next().getIndex();
				if (active.contains(k)) {
					for (int j = 0; j < maxC; j++) 
						model.setChannelActive(j, j == k);
					images.put(k, model.getSplitComponentImageAsTexture());
				} else {
					images.put(k, null);
				}
			}
			w = active.iterator();
			while (w.hasNext()) { //reset values.
				model.setChannelActive((Integer) w.next(), true);
			}
		} else {
			while (i.hasNext()) {
				k = i.next().getIndex();
				if (model.isChannelActive(k)) {
					for (int l = 0; l < maxC; l++)
						model.setChannelActive(l, k == l);

					images.put(k, model.getSplitComponentImageAsTexture());
					w = active.iterator();
					while (w.hasNext()) { //reset values.
						model.setChannelActive((Integer) w.next(), true);
					}
				} else {
					images.put(k, null);
				}
			}
		}
		view.createGridImage(false);
		view.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		return images;
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getCombinedGridImage()
	 */
	public BufferedImage getCombinedGridImage()
	{
		switch (model.getState()) {
		case NEW:
		case DISCARDED:
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or NEW"+
			" state.");
		}
		//view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
		if (!GREY_SCALE_MODEL.equals(model.getColorModel())) return null;
		List active = view.getActiveChannelsInGrid();
		BufferedImage image = null;
		Iterator i = active.iterator();
		for (int k = 0; k < model.getMaxC(); k++) {
			model.setChannelActive(k, false);
		}
		while (i.hasNext()) { //reset values.
			model.setChannelActive(((Integer) i.next()).intValue(), true);
		}
		if (active.size() != 0) {
			model.setColorModel(RGB_MODEL, false);
			image = model.getSplitComponentImage();
			model.setColorModel(GREY_SCALE_MODEL, false);
		}

		
		active = model.getActiveChannels();
		i = active.iterator();
		while (i.hasNext()) { //reset values.
			model.setChannelActive(((Integer) i.next()).intValue(), true);
		}
		return image;
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getDisplayedImage(boolean)
	 */
	public BufferedImage getDisplayedImage(boolean includeROI)
	{
		switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case DISCARDED:
				return null;
		}
		if (includeROI) {
			if (layers == null) return model.getDisplayedImage();
			return createImageWithROI(model.getDisplayedImage());//model.getBrowser().getRenderedImage());
		}
		return model.getDisplayedImage();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getDisplayedProjectedImage()
	 */
	public BufferedImage getDisplayedProjectedImage()
	{
		switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case DISCARDED:
				return null;
		}
		return model.getBrowser().getDisplayedProjectedImage();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getPixelsSizeX()
	 */
	public double getPixelsSizeX()
	{
		switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case DISCARDED:
				return -1;
		}
		return model.getPixelsSizeX();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getPixelsSizeY()
	 */
	public double getPixelsSizeY()
	{
		switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case DISCARDED:
				return -1;
		}
		return model.getPixelsSizeY();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getPixelsSizeZ()
	 */
	public double getPixelsSizeZ()
	{
		switch (model.getState()) {
			case NEW:
				throw new IllegalStateException(
						"This method can't be invoked in the NEW state.");
			case DISCARDED:
				return -1;
		}
		return model.getPixelsSizeZ();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getViewTitle()
	 */
	public String getViewTitle()
	{
		if (model.getState() == DISCARDED) return "";
		return view.getTitle();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getChannelMetadata(int)
	 */
	public ChannelData getChannelMetadata(int index)
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED state.");
		return model.getChannelData(index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getActiveChannels()
	 */
	public List getActiveChannels()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW "+
						"state.");
		}
		return model.getActiveChannels();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isUnitBar()
	 */
	public boolean isUnitBar()
	{
		if (model.getState() == DISCARDED) return false;
		return model.isUnitBar();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setUnitBar(boolean)
	 */
	public void setUnitBar(boolean b)
	{
		if (model.getState() == DISCARDED) return;
		model.getBrowser().setUnitBar(b);
		controller.setPreferences();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getHistoryState()
	 */
	public int getHistoryState()
	{
		return controller.getHistoryState();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getChannelColor(int)
	 */
	public Color getChannelColor(int index)
	{
		if (model.getState() == DISCARDED) return null;
		return model.getChannelColor(index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setUnitBarSize(double)
	 */
	public void setUnitBarSize(double size)
	{
		if (model.getState() == DISCARDED) return;
		model.getBrowser().setUnitBarSize(size);
		controller.setPreferences();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showUnitBarSelection()
	 */
	public void showUnitBarSelection()
	{
		if (model.getState() == DISCARDED) return;
		UnitBarSizeDialog d = new UnitBarSizeDialog(view);
		d.addPropertyChangeListener(controller);
		UIUtilities.centerAndShow(d);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#resetDefaults()
	 */
	public void resetDefaults()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException("The method cannot be invoked in " +
			"the DISCARDED state.");
		view.setLeftStatus();
		view.setPlaneInfoStatus();
		view.resetDefaults(); 
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUnitBarValue()
	 */
	public String getUnitBarValue()
	{
		if (model.getState() == DISCARDED) return "";
		return model.getBrowser().getUnitBarValue();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUnitBarSize()
	 */
	public double getUnitBarSize()
	{
		if (model.getState() == DISCARDED) return 0;
		return model.getBrowser().getUnitBarSize();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUnitBarColor()
	 */
	public Color getUnitBarColor()
	{
		if (model.getState() == DISCARDED) return null;
		if (model.getBrowser() == null) return null;
		return model.getBrowser().getUnitBarColor();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getImageIcon()
	 */
	public ImageIcon getImageIcon()
	{
		BufferedImage img = model.getImageIcon();
		if (img == null) return null;
		return new ImageIcon(img);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showLens()
	 */
	public void showLens()
	{
		if (model.getState() == DISCARDED) return;
		view.setLensVisible(!view.isLensVisible(), model.getTabbedIndex());
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getZoomedLensImage()
	 */
	public BufferedImage getZoomedLensImage()
	{
		if (model.getState() == DISCARDED) return null;
		return view.getZoomedLensImage();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showMenu(int, Component, Point)
	 */
	public void showMenu(int menuID, Component source, Point location)
	{
	    if (model.getState() == DISCARDED) return;
	    if (source == null) throw new IllegalArgumentException("No component.");
	    if (location == null) {
	        Point p = source.getLocation();
	        location = new Point(p.x+source.getWidth(), Math.abs(
	                p.y-source.getHeight()));
	    }
	    switch (menuID) {
	    case COLOR_PICKER_MENU:
	        if (model.getMaxC() == 1) showColorPicker(0);
	        else view.showMenu(menuID, source, location);
	        break;
	    case ACTIVITY_MENU:
	        model.activityOptions(source, location);
	        break;
	    default:
	        throw new IllegalArgumentException("Menu not supported.");
	    }
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getMaxX()
	 */
	public int getMaxX() 
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or" +
						" NEW state.");
		}
		return model.getMaxX();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getMaxY()
	 */
	public int getMaxY() 
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW" +
				"state.");
		}
		return model.getMaxY();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getSelectedIndex()
	 */
	public int getSelectedIndex() { return model.getTabbedIndex(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#playMovie(boolean, boolean, int)
	 */
	public void playMovie(boolean play, boolean visible, int index)
	{
	    switch (model.getState()) {
	    case NEW:
	    case LOADING_METADATA:
	    case DISCARDED:
	        return;
	    }
	    MoviePlayerDialog d = controller.getMoviePlayer();
	    boolean doClick = false;
	    if (visible) { // we have to play the movie
	        if (!d.isVisible()) {
	            controller.getAction(
	                    ImViewerControl.PLAY_MOVIE_T).setEnabled(false);
	            controller.getAction(
	                    ImViewerControl.PLAY_MOVIE_Z).setEnabled(false);
	            play = true;
	            UIUtilities.setLocationRelativeToAndShow(view, d);
	        }
	    } else {
	        if (d.isVisible()) {
	            controller.getAction(
	                    ImViewerControl.PLAY_MOVIE_T).setEnabled(true);
	            controller.getAction(
	                    ImViewerControl.PLAY_MOVIE_Z).setEnabled(true);
	            play = false;
	            d.setVisible(false);
	        } else {
	            switch (index) {
	            case PlayMovieAction.ACROSS_Z:
	                d.setZRange(model.getDefaultZ(), model.getMaxZ());
	                controller.getAction(
	                        ImViewerControl.PLAY_MOVIE_T).setEnabled(!play);
	                break;
	            case PlayMovieAction.ACROSS_T:
	                controller.getAction(
	                        ImViewerControl.PLAY_MOVIE_Z).setEnabled(!play);
	                break;
	            case PlayMovieAction.ACROSS_LIFETIME:
	                d.setBinRange(model.getSelectedBin(),
	                        model.getMaxLifetimeBin()-1);
	                controller.getAction(
	                        ImViewerControl.PLAY_LIFETIME_MOVIE).setEnabled(!play);
	                break;
	            default:
	                controller.getAction(
	                        ImViewerControl.PLAY_MOVIE_T).setEnabled(true);
	                controller.getAction(
	                        ImViewerControl.PLAY_MOVIE_Z).setEnabled(true);

	            }
	            doClick = true;
	            if (index != -1) d.setMovieIndex(index);
	            d.setTimeRange(model.getRealSelectedT(), model.getRealT()-1);
	        }
	    }

	    model.setPlayingMovie(play, index);
	    view.enableSliders(!play);
	    controller.getAction(ImViewerControl.CHANNEL_MOVIE).setEnabled(!play);
	    if (doClick) {
	        if (play) {
	            d.addPropertyChangeListener(
	                    MoviePlayerDialog.MOVIE_STATE_CHANGED_PROPERTY,
	                    controller);
	            d.doClick(MoviePlayerDialog.DO_CLICK_PLAY);
	        } else {
	            d.removePropertyChangeListener(
	                    MoviePlayerDialog.MOVIE_STATE_CHANGED_PROPERTY,
	                    controller);
	            d.doClick(MoviePlayerDialog.DO_CLICK_PAUSE);
	        }
	    } else {
	        d.removePropertyChangeListener(
	                MoviePlayerDialog.MOVIE_STATE_CHANGED_PROPERTY,
	                controller);
	    }
	    if (!play) {
	        if (view.isLensVisible()) view.setLensPlaneImage();
	        switch (view.getTabbedIndex()) {
	        case ImViewer.VIEW_INDEX:
	            view.createHistoryItem(null);
	            break;
	        case ImViewer.PROJECTION_INDEX:
	            view.createHistoryItem(view.getLastProjRef());
	        }
	        if (model.getState() != LOADING_IMAGE)
	            model.setState(READY);
	        fireStateChange();
	    }
	}

	/** 
	 * 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getGridImage()
	 */
	public BufferedImage getGridImage()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or NEW" +
						"state.");
		}
		return model.getBrowser().getGridImage();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getLensImageComponents(String)
	 */
	public List getLensImageComponents(String colorModel)
	{
		if (!view.hasLensImage()) return null;
		if (model.getTabbedIndex() != ImViewer.VIEW_INDEX) return null;
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or " +
				"NEW state.");
		}
		if (model.getColorModel().equals(GREY_SCALE_MODEL)) return null;
		List l = model.getActiveChannels();
		if (l.size() < 2) return null;
		Iterator i = l.iterator();
		int index;
		String oldColorModel = model.getColorModel();
		List<BufferedImage> images = new ArrayList<BufferedImage>(l.size());
		model.setColorModel(colorModel, false);
		while (i.hasNext()) {
			index = ((Integer) i.next()).intValue();
			for (int j = 0; j < model.getMaxC(); j++)
				model.setChannelActive(j, j == index); 
			images.add(view.createZoomedLensImage(
					model.getSplitComponentImage()));
		}
		model.setColorModel(oldColorModel, false);
		i = l.iterator();
		while (i.hasNext()) { //reset values.
			index = ((Integer) i.next()).intValue();
			model.setChannelActive(index, true);
		}
		return images;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isTextVisible()
	 */
	public boolean isTextVisible() { return model.isTextVisible(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setTextVisible(boolean)
	 */
	public void setTextVisible(boolean b)
	{
		model.setTextVisible(b);
		model.getBrowser().viewSplitImages();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showMeasurementTool(Point)
	 */
	public void showMeasurementTool(Point point)
	{
		//TODO: Review for HCS.
		if (!model.isHCSImage()) {
			postMeasurementEvent(null);
			return;
		}
		Collection measurements = model.getMeasurements();
		if (measurements == null || measurements.size() == 0) {
			postMeasurementEvent(null);
			return;
		}
		Map<JCheckBox, FileAnnotationData> boxes = 
			new LinkedHashMap<JCheckBox, FileAnnotationData>();
		JPanel p = new JPanel();
		p.setLayout(new BoxLayout(p, BoxLayout.Y_AXIS));
		Iterator i;
		if (measurements != null) {
			i = measurements.iterator();
			FileAnnotationData fa;
			JCheckBox box;
			Object object;
			while (i.hasNext()) {
				object = i.next();
				if (object instanceof FileAnnotationData) {
					fa = (FileAnnotationData) object;
					box = new JCheckBox(fa.getDescription());
					box.setSelected(true);
					p.add(box);
					boxes.put(box, fa);
				}
			}
		}
		
		if (boxes.size() == 0)  return;
		
		view.setMeasurementLaunchingStatus(true);
		MessageBox msg = new MessageBox(view, "Measurements", 
		"Select the measurements to display alongside the image.");
		msg.setNoText("Cancel");
		msg.setYesText("Display");
		msg.addBodyComponent(p);
		int option;
		if (point != null) option = msg.showMsgBox(point);
		else option = msg.centerMsgBox();
		List<FileAnnotationData> files = new ArrayList<FileAnnotationData>();
		
		if (option == MessageBox.YES_OPTION) {
			Entry entry;
			i = boxes.entrySet().iterator();
			while (i.hasNext()) {
				entry = (Entry) i.next();
				if (((JCheckBox) entry.getKey()).isSelected())
					files.add((FileAnnotationData) entry.getValue());
			}
			if (files.size() > 0) postMeasurementEvent(files);
		} else {
			view.setMeasurementLaunchingStatus(false);
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#addToView(JComponent)
	 */
	public void addToView(JComponent comp)
	{
		if (model.getState() != READY) return;
		if (comp == null) return;
		if (layers == null) layers = new ArrayList<JComponent>();
		layers.add(comp);
		view.setMeasurementLaunchingStatus(false);
		model.getBrowser().addComponent(comp, ImViewer.VIEW_INDEX, false);
		comp.setVisible(true);
		view.repaint();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#removeFromView(JComponent)
	 */
	public void removeFromView(JComponent comp)
	{
		if (model.getState() != READY) return;
		if (comp == null) return;
		if (layers != null) layers.remove(comp);
		model.getBrowser().removeComponent(comp, ImViewer.VIEW_INDEX);
		view.repaint();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#hasLens()
	 */
	public boolean hasLens()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException("This method cannot be invoked " +
			"in the DISCARDED state.");
		if (model.getState() != READY) return false;
		return view.hasLensImage(); 
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getZoomFactor()
	 */
	public double getZoomFactor()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException("This method cannot be invoked " +
			"in the DISCARDED state.");
		if (model.getState() != READY) return -1;
		return model.getZoomFactor();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isPlayingMovie()
	 */
	public boolean isPlayingMovie() { return model.isPlayingMovie(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isChannelRed(int)
	 */
	public boolean isChannelRed(int index)
	{
		return model.isColorComponent(Renderer.RED_BAND, index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isChannelGreen(int)
	 */
	public boolean isChannelGreen(int index)
	{
		return model.isColorComponent(Renderer.GREEN_BAND, index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isChannelBlue(int)
	 */
	public boolean isChannelBlue(int index)
	{
		return model.isColorComponent(Renderer.BLUE_BAND, index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isChannelActive(int)
	 */
	public boolean isChannelActive(int index)
	{
		return model.isChannelActive(index);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#copyRenderingSettings()
	 */
	public void copyRenderingSettings()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				//throw new IllegalStateException(
				//"This method can't be invoked in the DISCARDED, NEW state.");
				return;
		}
		try {
			model.saveRndSettings(false);
			model.copyRenderingSettings();
			saveBeforeCopy = true;
		} catch (Exception e) {
			saveBeforeCopy = false;
			failureToSave = true;
			Logger logger = ImViewerAgent.getRegistry().getLogger();
			LogMessage logMsg = new LogMessage();
			logMsg.print("Rendering Exception:");
			logMsg.println(e.getMessage());
			logMsg.print(e);
			logger.error(this, logMsg);
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#pasteRenderingSettings()
	 */
	public void pasteRenderingSettings()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				return;
		}
		if (!model.hasRndToPaste()) {
			return;
		}
		model.fireLoadRndSettingsToPaste();
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#reloadRenderingThumbs()
	 */
	public void reloadRenderingThumbs() {
	    model.reloadRenderingThumbs();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#hasSettingsToPaste()
	 */
	public boolean hasSettingsToPaste()
	{
		switch (model.getState()) {
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED state.");
		}
		return model.hasRndToPaste();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isHistoryShown()
	 */
	public boolean isHistoryShown()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException(
			"This method can't be invoked in the DISCARDED state.");
		return view.isHistoryShown();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showHistory(boolean)
	 */
	public void showHistory(boolean b)
	{
		if (model.getState() == DISCARDED)
			throw new IllegalStateException(
			"This method can't be invoked in the DISCARDED state.");
		view.showHistory(b);
		controller.setPreferences();
	}
	
	/** 
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#resetDefaultRndSettings()
     */
    public void resetDefaultRndSettings()
    {
    	if (model.getState() == DISCARDED)
			throw new IllegalStateException(
			"This method can't be invoked in the DISCARDED state.");
    	//addHistoryItem();
		model.resetDefaultRndSettings();
		view.resetDefaults();
		renderXYPlane();
    }
    
    /** 
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#saveRndSettings(boolean)
     */
    public void saveRndSettings(boolean post)
    {
    	try {
    		model.saveRndSettings(true);
    		failureToSave = false;
		} catch (Exception e) {
			UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();
			un.notifyInfo("Save settings", "Cannot save rendering settings. ");
			Logger logger = ImViewerAgent.getRegistry().getLogger();
			LogMessage msg = new LogMessage();
	        msg.print("Save rendering settings");
	        msg.print(e);
	        logger.error(this, msg);
	        failureToSave = true;
	        //post event indicating no settings to save
		}
		if (post) {
		      EventBus bus = ImViewerAgent.getRegistry().getEventBus();
		        bus.post(new RndSettingsCopied(Arrays.asList(model.getImageID()),
		                getPixelsID()));
		        fireStateChange();
		}
    }
    
    /** 
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#saveRndSettings()
     */
    public void toFront()
    {
    	if (model.getState() == DISCARDED) return;
    	controller.toFront();
    }
    
    /** 
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#getMovieIndex()
     */
	public int getMovieIndex()
	{
		if (model.getState() == DISCARDED || !model.isPlayingMovie() ||
			model.isPlayingChannelMovie()) return -1;
		return model.getMovieIndex();
	}

	 /** 
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#getActiveChannelsInGrid()
     */
	public List getActiveChannelsInGrid()
	{
		if (model.getState() == DISCARDED) return null;
		return view.getActiveChannelsInGrid();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showPreferences()
	 */
	public void showPreferences()
	{
		ViewerPreferences pref = ImViewerFactory.getPreferences();
		PreferencesDialog d = new PreferencesDialog(view, pref);
		d.addPropertyChangeListener(controller);
		UIUtilities.centerAndShow(d);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setRenderingSettings(Map, long)
	 */
	public void setRenderingSettings(Map map, long userID)
	{
		if (model.getState() == DISCARDED) return;
		model.setRenderingSettings(map);
		if (userID >= 0) {
			if (map != null) {
				Entry entry;
				Iterator i = map.entrySet().iterator();
				ExperimenterData exp;
				while (i.hasNext()) {
					entry = (Entry) i.next();
					exp = (ExperimenterData) entry.getKey();
					if (exp.getId() == userID) {
						model.setUserSettings(exp);
						break;
					} 
				}
			}
		} else {
			view.showUsersList();
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#retrieveRelatedSettings(Component, Point)
	 */
	public void retrieveRelatedSettings(Component source, Point location)
	{
		if (model.getState() == DISCARDED) return;
		Map m = model.getRenderingSettings();
		view.setLocationAndSource(source, location);
		if (m == null) model.fireRenderingSettingsRetrieval();
		else view.showUsersList();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setGridMagnificationFactor(double)
	 */
	public void setGridMagnificationFactor(double factor)
	{
		if (model.getTabbedIndex() != ImViewer.GRID_INDEX)  return;
		view.setGridMagnificationFactor(factor);
		model.getBrowser().setGridRatio(factor);
		if (view.isLensVisible()) {
			view.setImageZoomFactor((float) model.getBrowser().getGridRatio());
			view.scrollLens();	
		}
		/*
		if (view.isLensVisible() && 
				model.getTabbedIndex() == ImViewer.GRID_INDEX) {
			view.setImageZoomFactor((float) model.getBrowser().getGridRatio());
			view.scrollLens();	
		}
		*/
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setUserRndSettings(ExperimenterData)
	 */
	public void setUserRndSettings(ExperimenterData exp)
	{
		if (model.getState() == DISCARDED)
			throw new IllegalArgumentException("This method cannot be invoked" +
					" in the DISCARDED state.");
		try {
			view.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
			model.setUserSettings(exp);
			view.resetDefaults();
			renderXYPlane();
		} catch (Exception e) {
			UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();
			un.notifyInfo("Set User rendering settings", "Could not apply " +
					"the settings set by "+exp.getFirstName()+
					" "+exp.getLastName());
		}
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUserDetails()
	 */
	public ExperimenterData getUserDetails()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalArgumentException("This method cannot be invoked" +
					" in the DISCARDED state.");
		return model.getUserDetails();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showView(int)
	 */
	public void showView(int index)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
					"This method can't be invoked in the DISCARDED or" +
					"NEW state.");
		}
		if (index == RENDERER_INDEX || index == METADATA_INDEX) {
			view.showRenderer(false, index);
			controller.setPreferences();
		} else {
			view.showView(index);
			setSelectedPane(index);
		}
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setOriginalRndSettings()
	 */
	public void setOriginalRndSettings()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalArgumentException("This method cannot be invoked" +
					" in the DISCARDED state.");
		model.setOriginalRndSettings();
		view.resetDefaults();
		renderXYPlane();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#projectImage(ProjectionRef)
	 */
	public void projectImage(ProjectionRef ref)
	{
		if (model.getState() == DISCARDED)
			throw new IllegalArgumentException("This method cannot be invoked" +
					" in the DISCARDED state.");

		if (model.getTabbedIndex() != PROJECTION_INDEX) return;
		if (ref == null) 
			throw new IllegalArgumentException("No projection object");
		model.fireImageProjection(ref.getStartZ(), ref.getEndZ(),
				view.getProjectionStepping(), view.getProjectionType(),
				view.getProjectionTypeName(), ref);
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setProjectionPreview(Object)
	 */
	public void setProjectionPreview(Object image)
	{
		if (image == null) {
			UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();
			un.notifyInfo("Projection preview", "An error has occurred " +
					"while projecting the data.");
			return;
		}
		if (model.getTabbedIndex() != PROJECTION_INDEX) return;
		model.setRenderProjected(image);
		
		view.setLeftStatus();
		view.setPlaneInfoStatus();	
		if (!model.isPlayingMovie() && !model.isPlayingChannelMovie()) {
			if (view.isLensVisible()) view.setLensPlaneImage();
			view.createHistoryItem(model.getLastProjRef());
		}
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setContainers(Collection)
	 */
	public void setContainers(Collection containers)
	{
		/*
		if (model.getState() != LOADING_PROJECTION_DATA)
			throw new IllegalArgumentException("This method can only be " +
					"invoked in the LOADING_PROJECTION_DATA state.");
					*/
		//Create a modal dialog.	
		if (model.getTabbedIndex() != PROJECTION_INDEX) return;
		model.setContainers(containers);
		fireStateChange();
		if (projDialog == null) showProjectionDialog();
		else projDialog.setContainers(model.getContainers());
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#loadContainers()
	 */
	public void loadContainers()
	{
		if (model.getState() == DISCARDED)
			throw new IllegalArgumentException("This method cannot be invoked" +
					" in the DISCARDED state.");
		if (model.getTabbedIndex() != PROJECTION_INDEX) return;
		if (model.getContainers() == null) {
			model.fireContainersLoading();
			fireStateChange();
		} else showProjectionDialog();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setProjectedImage(ImageData, List, List, boolean)
	 */
	public void setProjectedImage(ImageData image, List<Integer> indexes,
				List<DataObject> containers, boolean applySettings)
	{
		UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();
		String message;
		if (image == null) {
			message = "An error occurred while creating the projected image.";
			un.notifyInfo("Projection", message);
			model.setState(READY);
		} else {
			if (applySettings) 
				model.fireProjectedRndSettingsCreation(indexes, image);
			else
				notifyProjection("The projected image has been " +
						"successfully created.", image);
			if (containers != null) {
				EventBus bus = ImViewerAgent.getRegistry().getEventBus();
				bus.post(new NodeToRefreshEvent(containers, true));
			}
		}
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setProjectedRenderingSettings(Boolean, ImageData)
	 */
	public void setProjectedRenderingSettings(Boolean result, ImageData image)
	{
		String message;
		if (result)
			message = "The projected image and the rendering settings\nhave" +
					" been successfully created.";
		else
			message = "An error has occurred while copying the " +
			"rendering settings of the projected image.";
		
		notifyProjection(message, image);
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setContext(DataObject, DataObject)
	 */
	public void setContext(DataObject parent, DataObject grandParent)
	{
		if (model.getState() == DISCARDED) return;
		model.setContext(parent, grandParent);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setPlaneInfo(Collection)
	 */
	public void setPlaneInfo(Collection collection)
	{
		if (collection == null) return;
		model.setPlaneInfo(collection);
		view.setPlaneInfoStatus();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setImageData(ImageData)
	 */
	public void setImageData(ImageData data)
	{
		if (model.getState() != LOADING_IMAGE_DATA)
			throw new IllegalArgumentException("This method can only be " +
					"invoked in the LOADING_IMAGE_DATA.");
		if (data == null)
			throw new IllegalArgumentException("No image to set.");
		model.setImageData(data);
		view.setImageData();
		if (model.getMetadataViewer() != null)
			model.getMetadataViewer().addPropertyChangeListener(controller);
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setSelectedPane(int)
	 */
	public void setSelectedPane(int index)
	{
		if (model.getState() == DISCARDED) return;
		int oldIndex = model.getTabbedIndex();
		if (oldIndex == index) return;
		
		view.setSelectedPane(index);
		if (oldIndex == ImViewer.GRID_INDEX) {
			int key = ColorModelAction.RGB_MODEL;
			if (GREY_SCALE_MODEL.equals(colorModel))
				key = ColorModelAction.GREY_SCALE_MODEL;
			setColorModel(key);
		}
		
		firePropertyChange(TAB_SELECTION_PROPERTY, 
				Boolean.valueOf(false), Boolean.valueOf(true));
		if (oldIndex == ImViewer.PROJECTION_INDEX 
				&& index == ImViewer.VIEW_INDEX) {
			//check if settings have changed.
			//model.setLastSettingsRef(oldIndex);
			renderXYPlane();
		} else if (index == ImViewer.PROJECTION_INDEX && 
				oldIndex == ImViewer.VIEW_INDEX) {
			//model.setLastSettingsRef(oldIndex);
			double f = model.getZoomFactor();
			if (model.getBrowser().hasProjectedPreview()) {
				RndProxyDef def = model.getLastProjDef();
				boolean b = true;
				if (def != null) b = model.isSameSettings(def, false);
				if (!b || !isSameProjectionParam())
					renderXYPlane();
				else {
					BufferedImage image = model.getProjectedImage();
					if (image != null) {
						int x = (int) (model.getMaxX()*f);
						if (x != image.getWidth())
							model.setZoomFactor(f, false);
					}
				}
			} else {
				model.getBrowser().setZoomFactor(model.getZoomFactor(), false);
			}
		} else {
			renderXYPlane();
		}
		model.getBrowser().getUI().setVisible(true);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#loadMetadata()
	 */
	public void loadMetadata()
	{
		if (model.getState() == DISCARDED) return;
		model.loadMetadata();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setColorModel(int)
	 */
	public void setCompressionLevel()
	{
		switch (model.getState()) {
			case DISCARDED:
			case PROJECTION_PREVIEW:
				throw new IllegalArgumentException("This method cannot be " +
				"invoked in the DISCARDED or PROJECTION_PREVIEW state.");
		}
		int old = view.convertCompressionLevel();
		int index = view.getUICompressionLevel();
		if (old == index) return;
		view.setCompressionLevel(index);
		if (!model.isLargePlane())
			ImViewerFactory.setCompressionLevel(index);
		renderXYPlane();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setColorModel(int)
	 */
	public void clearHistory()
	{
		switch (model.getState()) {
			case DISCARDED:
			case PROJECTION_PREVIEW:
				throw new IllegalArgumentException("This method cannot be " +
				"invoked in the DISCARDED or PROJECTION_PREVIEW state.");
		}
		model.clearHistory();
		view.clearHistory();
		setSelectedPane(VIEW_INDEX);
		//renderXYPlane();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isOriginalSettings()
	 */
	public boolean isOriginalSettings()
	{
		switch (model.getState()) {
			case DISCARDED:
				throw new IllegalArgumentException("This method cannot be " +
				"invoked in the DISCARDED state.");
		}
		return model.isOriginalSettings();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setSettingsToPaste(RndProxyDef)
	 */
	public void setSettingsToPaste(RndProxyDef rndProxyDef)
	{
		if (model.getState() != PASTING)
			throw new IllegalArgumentException("This method should be " +
					"invoked in the PASTING state.");
		try {
			model.resetMappingSettings(rndProxyDef);
			saveRndSettings(false);
			view.resetDefaults();
			renderXYPlane();
		} catch (Exception e) {
			UserNotifier un = ImViewerAgent.getRegistry().getUserNotifier();

			Logger logger = ImViewerAgent.getRegistry().getLogger();
			LogMessage logMsg = new LogMessage();
			logMsg.print("Rendering Exception:");
			logMsg.println(e.getMessage());
			logMsg.print(e);
			logger.error(this, logMsg);
			un.notifyError("Paste Rendering settings", "An error occurred " +
					"while pasting the rendering settings.", e);
			view.setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
		}
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getSortedChannelData()
	 */
	public List<ChannelData> getSortedChannelData()
	{
		return model.getChannelData();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#showColorPicker(int)
	 */
	public void showColorPicker(int index)
	{
		if (model.getState() == DISCARDED) return;
		controller.showColorPicker(index);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#loadAllContainers()
	 */
	public void loadAllContainers()
	{
		if (model.getState() == DISCARDED) return;
		model.loadAllContainers();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getUnitInRefUnits()
	 */
	public double getUnitInRefUnits() { return model.getUnitInRefUnits(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#makeMovie()
	 */
	public void makeMovie()
	{
		if (model.getState() == DISCARDED) return;
		model.makeMovie();
	}

	/** Build the view.*/
	private void buildView()
	{
		int index = UnitBarSizeAction.getDefaultIndex(
				EditorUtil.transformSize(5*getPixelsSizeX()).getValue());
		setUnitBarSize(UnitBarSizeAction.getValue(index));
		view.setDefaultScaleBarMenu(index);
		colorModel = model.getColorModel();
		view.buildComponents();
		view.onRndLoaded();
		if (model.isSeparateWindow()) {
			view.setOnScreen();
			view.toFront();
			view.requestFocusInWindow();
		} else {
			postViewerCreated(true, false);;
		}
		if (ImViewerAgent.isFastConnection())
			model.firePlaneInfoRetrieval();
		view.setLeftStatus();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#onRndLoaded(boolean)
	 */
	public void onRndLoaded(boolean reload)
	{
		if (model.getState() == DISCARDED) return;
		model.onRndLoaded();
		if (!reload) {
			if (model.isBigImage()) {
				view.setCompressionLevel(ToolBar.LOW);
				view.resetCompressionLevel(view.convertCompressionLevel());
				model.fireBirdEyeViewRetrieval(true);
				fireStateChange();
				return;
			}
			buildView();
		} else {
			//TODO
			//clean history, reset UI element
			model.resetHistory();
			view.switchRndControl();
		}
		renderXYPlane();
		fireStateChange();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#onChannelSelection(int)
	 */
	public void onChannelSelection(int index)
	{
		int uiIndex = ImViewerUI.ALL_VIEW;
		firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
				Integer.valueOf(index-1), Integer.valueOf(index));
		view.setChannelsSelection(uiIndex);
		postActiveChannelSelection(ChannelSelection.CHANNEL_SELECTION);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#allowSplitView()
	 */
	public boolean allowSplitView()
	{
		switch (model.getState()) {
			case DISCARDED:
			case NEW:
				throw new IllegalArgumentException("This method cannot be " +
				"invoked in the DISCARDED or NEW state.");
			}
		return model.allowSplitView();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#selectAllChannels(boolean)
	 */
	public void selectAllChannels(boolean selection)
	{
		String cm = model.getColorModel();
		if (ImViewer.GREY_SCALE_MODEL.equals(cm)) return;
		for (int i = 0; i < model.getMaxC(); i++) {
			model.setChannelActive(i, selection);
			firePropertyChange(CHANNEL_ACTIVE_PROPERTY, 
					Integer.valueOf(i-1), Integer.valueOf(i));
			
			model.setSelectedChannel(i);		
		}
		view.setChannelsSelection(ImViewerUI.ALL_VIEW);
		renderXYPlane();
		postActiveChannelSelection(ChannelSelection.CHANNEL_SELECTION);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setMeasurements(Collection)
	 */
	public void setMeasurements(Collection result)
	{
		if (model.getState() == DISCARDED) return;
		model.setMeasurements(result);
		
		Collection measurements = model.getMeasurements();
		boolean enabled = true;
		if (measurements == null || measurements.size() == 0) {
			enabled = false;
		} else {
			Iterator i;
			i = measurements.iterator();
			FileAnnotationData fa;
			JCheckBox box;
			Object object;
			List<Object> l = new ArrayList<Object>();
			while (i.hasNext()) {
				object = i.next();
				if (object instanceof FileAnnotationData)
					l.add(object);
			}
			if (l.size() == 0) enabled = false;
		}
		
		if (!enabled) {
			Action a = controller.getAction(ImViewerControl.MEASUREMENT_TOOL);
			a.setEnabled(false);
		}
		//Notify UI to build overlays 
		view.buildOverlays();
		
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#scrollToViewport(Rectangle)
	 */
	public void scrollToViewport(Rectangle bounds)
	{
		if (bounds == null) return;
		model.getBrowser().scrollTo(bounds, false);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#createImageFromTexture(int, boolean includeROI)
	 */
	public BufferedImage createImageFromTexture(int type, boolean includeROI)
	{
		switch (model.getState()) {
			case LOADING_IMAGE:
			case DISCARDED:
				return null;
		}
		if (!ImViewerAgent.hasOpenGLSupport()) return null;

		BufferedImage img = model.getBrowser().createImageFromTexture(type);
		if (img == null) return null;
        
		if (includeROI) {
			createImageWithROI(img);
		}
		return img;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isMappedImageRGB(List)
	 */
	public boolean isMappedImageRGB(List channels)
	{
		switch (model.getState()) {
			case DISCARDED:
				return false;
		}
		return model.isMappedImageRGB(channels);
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#renderOverlays(int, boolean)
	 */
	public void renderOverlays(int index, boolean selected)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or " +
						"NEW state.");
		}
		view.renderOverlays(index, selected);
		if (!view.isOverlayActive()) return;
		Map<Long, Integer> m = null;
		if (index < 0) {
			if (selected) m = view.getSelectedOverlays();
		} else m = view.getSelectedOverlays();
		model.renderOverlays(m);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#renderOverlays(int, boolean)
	 */
	public void onChannelColorChanged(int index)
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				throw new IllegalStateException(
						"This method can't be invoked in the DISCARDED or " +
						"NEW state.");
		}
		view.setChannelColor(index, model.getChannelColor(index));
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isBigImage()
	 */
	public boolean isBigImage()
	{
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
			case LOADING_IMAGE_DATA:
				return false;
		}
		return model.isBigImage();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#refresh()
	 */
	public void refresh()
	{
		view.refresh();
		renderXYPlane();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#close(boolean)
	 */
	public void close(boolean notifyUser)
	{
		if (model.getState() == DISCARDED) return;
		if (!view.isVisible()) return;
		if (notifyUser) postViewerCreated(false, false);
		switch (model.getState()) {
			//case DISCARDED:
			default:
				controller.setPreferences();
				//tmp store compression
				if (!model.isLargePlane())
				ImViewerFactory.setCompressionLevel(
						view.getUICompressionLevel());
				if (!saveOnClose(notifyUser)) {
					return;
				}
				if (notifyUser) {
					postViewerState(ViewerState.CLOSE);
					ImViewerRecentObject object = new ImViewerRecentObject(
						model.getSecurityContext(),
						model.getImageID(), model.getImageTitle(),
						getImageIcon());
					firePropertyChange(RECENT_VIEWER_PROPERTY, null, object);
				}
				
		}
		view.setVisible(false);
		view.dispose();
		discard();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#detach()
	 */
	public void detach()
	{
		if (model.getState() == DISCARDED) return;
		if (model.isSeparateWindow()) return; //Option not available.
		postViewerCreated(false, true);
		model.setSeparateWindow(true);
		view.rebuild();
		view.setOnScreen();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#canAnnotate()
	 */
	public boolean canAnnotate()
	{
		if (isUserOwner()) return true;
		return model.getImage().canAnnotate();
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isUserOwner()
	 */
	public boolean isUserOwner()
	{
		return model.isUserOwner();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setRangeAllChannels(boolean)
	 */
	public void setRangeAllChannels(boolean absolute)
	{
		model.setRangeAllChannels(absolute);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setOwnerSettings()
	 */
	public void setOwnerSettings()
	{
		Map m = model.getRenderingSettings();
		if (m == null) model.fireOwnerSettingsRetrieval();
		else setRenderingSettings(m, model.getOwnerID());
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#includeROI()
	 */
	public boolean includeROI()
	{
		if (layers == null) return false;
		if (ImViewerAgent.hasOpenGLSupport()) return false;
		Iterator<JComponent> i = layers.iterator();
		while (i.hasNext()) {
			if (i.next() instanceof DrawingCanvasView) return true;
		}
		return false;
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#displayFLIMResults(Map)
	 */
	public void displayFLIMResults(Map<FileAnnotationData, File> results)
	{
		if (results == null) return;
		switch (model.getState()) {
			case NEW:
			case DISCARDED:
				return;
		}
		IconManager icons = IconManager.getInstance();
		FLIMResultsDialog d = new FLIMResultsDialog(view, 
				EditorUtil.getPartialName(model.getImageName()),
				icons.getIcon(IconManager.FLIM_48), results);
		d.addPropertyChangeListener(new PropertyChangeListener() {
			
			public void propertyChange(PropertyChangeEvent evt) {
				String name = evt.getPropertyName();
				if (FLIMResultsDialog.SAVED_FLIM_RESULTS_PROPERTY.equals(
						name)){
					boolean b = (
							(Boolean) evt.getNewValue()).booleanValue();
					UserNotifier un = 
						ImViewerAgent.getRegistry().getUserNotifier();
					if (b) {
						un.notifyInfo("Saving Results", "The file has " +
								"successfully been saved.");
					} else {
						un.notifyInfo("Saving Results", "An error " +
						"occurred while saving the results.");
					}
				}
			}
		});
		UIUtilities.centerAndShow(d);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setBirdEyeView(Object)
	 */
	public void setBirdEyeView(BufferedImage image, boolean scaled)
	{
		switch (model.getState()) {
			case LOADING_BIRD_EYE_VIEW:
				boolean set = false;
				if (!view.isVisible()) {
					buildView();
					set = true;
				}
				model.setBirdEyeView(image, scaled);
				if (set)
					controller.setZoomFactor(model.getSelectedResolutionLevel());
				return;
		}
		
		model.setBirdEyeView(image, scaled);
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getRows()
	 */
	public int getRows() { return model.getRows(); }

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getColumns()
	 */
	public int getColumns() { return model.getColumns(); }
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getTiles()
	 */
	public Map<Integer, Tile> getTiles()
	{
		if (model.getState() == DISCARDED) return null;
		return model.getTiles();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#setTile(int)
	 */
	public void setTileCount(int count)
	{
		if (model.getState() == DISCARDED) return;
		model.getBrowser().getUI().repaint();
		if (model.isTileLoaded(count)) {
			view.addComponentListener(controller);
			model.setState(READY);
			fireStateChange();
		}
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#loadTiles(Rectangle)
	 */
	public void loadTiles(Rectangle region)
	{
		if (model.getState() == DISCARDED) return;
		if (region == null) 
			region = model.getBrowser().getVisibleRectangle();
		Map<Integer, Tile> tiles = getTiles();
    	if (tiles == null) return;
    	//invalidate images.
    	Dimension d = model.getTileSize();
    	int width = d.width;
    	int height = d.height;
    	int cs = region.x/width;
    	int rs = region.y/height;
    	int ih = region.width/width;
    	int iv = region.height/height;
    	int columns = getColumns();
    	int index;
    	Tile t;
    	
    	int h = rs+iv+1;
    	int w = cs+ih+1;
    	cs = cs-1;
    	rs = rs-1;
    	if (cs < 0) cs = 0;
    	if (rs < 0) rs = 0;
    	List<Tile> l = new ArrayList<Tile>();
    	List<Tile> toKeep = new ArrayList<Tile>();
    	for (int i = rs; i <= h; i++) {
			for (int j = cs; j <= w; j++) {
				index = i*columns+j;
				t = tiles.get(index);
				if (t != null) {
					if (t.isImageLoaded()) {
						if (!toKeep.contains(t))
							toKeep.add(t);
					} else {
						if (!l.contains(t))
							l.add(t);
					}
				}
			}
		}
    	List<Tile> toClear = new ArrayList<Tile>();
    	Iterator<Tile> k = tiles.values().iterator();
    	while (k.hasNext()) {
			t = k.next();
			if (t.isImageLoaded() && !toClear.contains(t) && 
					!toKeep.contains(t))
				toClear.add(t);
		}
    	model.clearTileImages(toClear);
		if (l.size() > 0) {
			view.removeComponentListener(controller);
			model.fireTileLoading(l);
			fireStateChange();
		}
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getTiledImageSizeX()
	 */
	public int getTiledImageSizeX()
	{ 
		return model.getTiledImageSizeX();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getTiledImageSizeY()
	 */
	public int getTiledImageSizeY()
	{ 
		return model.getTiledImageSizeY();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#cancelInit()
	 */
	public void cancelInit()
	{
		switch (model.getState()) {
			case LOADING_RND:
				if (model.isBigImage()) {
					model.cancelBirdEyeView(); 
					view.dispose();
					fireStateChange();
				} else {
					model.cancelRendering();
					discard();
					fireStateChange();
				}
				break;
			case LOADING_BIRD_EYE_VIEW:
				model.cancelBirdEyeView(); 
				view.dispose();
				fireStateChange();
		}
	}

	/**
	 * Returns the security context.
	 * 
	 * @return See above.
	 */
	public SecurityContext getSecurityContext()
	{ 
		return model.getSecurityContext();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#isCompressed()
	 */
	public boolean isCompressed()
	{
		return model.getCompressionLevel() != RenderingControl.UNCOMPRESSED;
	}

	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#onUpdatedChannels(List)
	 */
	public void onUpdatedChannels(List<ChannelData> channels)
	{
		model.setChannels(channels);
		view.onChannelUpdated();
	}
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getDisplayMode()
	 */
	public int getDisplayMode() { return model.getDisplayMode(); }
	
	/** 
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getPixelsID()
	 */
	public long getPixelsID() { return model.getPixelsID(); }
	
	/**
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getSelectedResolutionLevel()
	 */
	public int getSelectedResolutionLevel()
	{
		return model.getSelectedResolutionLevel();
	}
	
	/**
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getResolutionLevels()
	 */
	public int getResolutionLevels()
	{
		return model.getResolutionLevels();
	}

	/**
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getResolutionLevels()
	 */
	public int getSelectedBin() { return model.getSelectedBin(); }

	/**
	 * Implemented as specified by the {@link ImViewer} interface.
	 * @see ImViewer#getMaxLifetimeBin()
	 */
	public int getMaxLifetimeBin() { return model.getMaxLifetimeBin(); }

	/**
     * Implemented as specified by the {@link ImViewer} interface.
     * @see ImViewer#getRealSelectedT()()
     */
    public int getRealSelectedT() { return model.getRealSelectedT(); }

	/** 
	 * Overridden to return the name of the instance to save. 
	 * @see #toString()
	 */
	public String toString()
	{ 
		return "Image's Settings: "+EditorUtil.truncate(model.getImageName());
	}

}
