org.openmicroscopy.shoola.agents.imviewer.view.ImViewerUI.java Source code

Java tutorial

Introduction

Here is the source code for org.openmicroscopy.shoola.agents.imviewer.view.ImViewerUI.java

Source

/*
 * org.openmicroscopy.shoola.agents.iviewer.view.ImViewerUI
 *
 *------------------------------------------------------------------------------
 *  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.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.Insets;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.Toolkit;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.HierarchyBoundsAdapter;
import java.awt.event.HierarchyBoundsListener;
import java.awt.event.HierarchyEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Enumeration;
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.AbstractButton;
import javax.swing.Action;
import javax.swing.BoxLayout;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JSeparator;
import javax.swing.JSplitPane;
import javax.swing.JTabbedPane;

//Third-party libraries
import info.clearthought.layout.TableLayout;
import com.sun.opengl.util.texture.TextureData;

//Application-internal dependencies
import omero.model.PlaneInfo;

import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
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.UnitBarSizeAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.ViewerAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.ZoomAction;
import org.openmicroscopy.shoola.agents.imviewer.actions.ZoomGridAction;
import org.openmicroscopy.shoola.agents.imviewer.browser.Browser;
import org.openmicroscopy.shoola.agents.imviewer.util.ChannelColorMenuItem;
import org.openmicroscopy.shoola.agents.imviewer.util.HistoryItem;
import org.openmicroscopy.shoola.agents.imviewer.util.ImagePaintingFactory;
import org.openmicroscopy.shoola.agents.imviewer.util.PlaneInfoComponent;
import org.openmicroscopy.shoola.agents.imviewer.util.player.MoviePlayerDialog;
import org.openmicroscopy.shoola.agents.metadata.view.MetadataViewer;
import org.openmicroscopy.shoola.agents.util.EditorUtil;
import org.openmicroscopy.shoola.env.data.model.ProjectionParam;
import org.openmicroscopy.shoola.env.rnd.data.ResolutionLevel;
import org.openmicroscopy.shoola.env.ui.TaskBar;
import org.openmicroscopy.shoola.env.ui.TopWindow;
import org.openmicroscopy.shoola.util.file.modulo.ModuloInfo;
import org.openmicroscopy.shoola.util.ui.ClosableTabbedPane;
import org.openmicroscopy.shoola.util.ui.ClosableTabbedPaneComponent;
import org.openmicroscopy.shoola.util.ui.ColorCheckBoxMenuItem;
import org.openmicroscopy.shoola.util.ui.LoadingWindow;
import org.openmicroscopy.shoola.util.ui.UIUtilities;
import org.openmicroscopy.shoola.util.ui.UnitsObject;
import org.openmicroscopy.shoola.util.ui.lens.LensComponent;
import org.openmicroscopy.shoola.util.ui.tdialog.TinyDialog;
import pojos.ChannelData;
import pojos.DataObject;
import pojos.GroupData;
import pojos.ImageData;

/** 
 * The {@link ImViewer} view.
 * Embeds the {@link Browser}. Also provides a menu bar, a status bar and a 
 * panel hosting various controls.
 *
 * @author Jean-Marie Burel     
 * <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
 * @since OME2.2
 */
class ImViewerUI extends TopWindow {

    /** Indicates to update the channel buttons composing the grid view. */
    static final int GRID_ONLY = 0;

    /** Indicates to update the channel buttons composing the main view. */
    static final int VIEW_ONLY = 1;

    /** Indicates to update the channel buttons composing the main view. */
    static final int PROJECTION_ONLY = 2;

    /**
     * Indicates to update the channel buttons composing the grid view,
     * the projection view and the main view. 
     */
    static final int ALL_VIEW = 3;

    /** Indicates that only the image is displayed. */
    static final int NEUTRAL = 0;

    /** Indicates that the image and the history are displayed. */
    static final int HISTORY = 1;

    /** Indicates that the image and the renderer are displayed. */
    static final int RENDERER = 2;

    /** Indicates that the image, the history and the renderer are displayed. */
    static final int HISTORY_AND_RENDERER = 3;

    /** Number of pixels added to the height of an icon. */
    private static final int ICON_EXTRA = 4;

    /** Indicates the percentage of the screen to use to display the viewer. */
    private static final double SCREEN_RATIO = 0.9;

    /** The available colors for the background color of the canvas. */
    private static Map<Color, String> backgrounds;

    static {
        backgrounds = new LinkedHashMap<Color, String>();
        backgrounds.put(ImagePaintingFactory.DEFAULT_BACKGROUND, ImagePaintingFactory.DEFAULT_BACKGROUND_NAME);
        backgrounds.put(Color.WHITE, "White");
        backgrounds.put(Color.BLACK, "Black");
        backgrounds.put(Color.GRAY, "Grey");
        backgrounds.put(Color.LIGHT_GRAY, "Light Grey");
    }

    private static final String SCALE_BAR_TEXT = "Scale bar length (in ";
    /** Reference to the Control. */
    private ImViewerControl controller;

    /** Reference to the Model. */
    private ImViewerModel model;

    /** The status bar. */
    private StatusBar statusBar;

    /** Lens component which will control all behavior of the lens. */
    private LensComponent lens;

    /** The tool bar. */
    private ToolBar toolBar;

    /** The control pane. */
    private ControlPane controlPane;

    /** Group hosting the items of the <code>Zoom</code> menu. */
    private ButtonGroup zoomingGroup;

    /** Group hosting the items of the <code>Color Model</code> menu. */
    private ButtonGroup colorModelGroup;

    /** The loading window. */
    private LoadingWindow loadingWindow;

    /** Tab pane hosting the various panel. */
    private ClosableTabbedPane tabs;

    /** The component displaying the history. */
    private HistoryUI historyUI;

    /**
     * Split component used to display the image in the top section and the
     * history component in the bottom one.
     */
    private JSplitPane historySplit;

    /**
     * Split component used to display the renderer component on the left hand
     * side of the pane.
     */
    private JSplitPane rendererSplit;

    /** 
     * One out of the following list: 
     * {@link #NEUTRAL}, {@link #HISTORY}, {@link #RENDERER} and
     * {@link #HISTORY_AND_RENDERER}.
     */
    private int displayMode;

    /** Item used to control show or hide the renderer. */
    private JCheckBoxMenuItem rndItem;

    /** Item used to control show or hide the metadata. */
    private JCheckBoxMenuItem metadataItem;

    /** Item used to control show or hide the history. */
    private JCheckBoxMenuItem historyItem;

    /** The dimension of the main component i.e. the tab pane. */
    private Dimension restoreSize;

    /** Listener to the bounds of the container. */
    private HierarchyBoundsAdapter boundsAdapter;

    /** The height of the icons in the tab pane plus 2 pixels. */
    private int tabbedIconHeight;

    /** The menu displaying the users who viewed the image. */
    private UsersPopupMenu usersMenu;

    /** The default insets of a split pane. */
    private Insets refInsets;

    /** Group hosting the possible background colors. */
    private ButtonGroup bgColorGroup;

    /** Group hosting the possible scale bar length. */
    private ButtonGroup scaleBarGroup;

    /** The source invoking the {@link #usersMenu}. */
    private Component source;

    /** The location where to pop up the {@link #usersMenu}. */
    private Point location;

    /** The zoom menu. */
    private JMenu zoomMenu;

    /** The zoom grid menu. */
    private JMenu zoomGridMenu;

    /** Group hosting the items of the <code>ZoomGrid</code> menu. */
    private ButtonGroup zoomingGridGroup;

    /** The panel hosting the view. */
    private ClosableTabbedPaneComponent viewPanel;

    /** The panel hosting the view. */
    private ClosableTabbedPaneComponent gridViewPanel;

    /** The panel hosting the view. */
    private ClosableTabbedPaneComponent projectionViewPanel;

    /** The object displaying the plane information, one per channel. */
    private Map<Integer, PlaneInfoComponent> planes;

    /** The central component. */
    private JComponent mainComponent;

    /** The default index of the scale bar. */
    private int defaultIndex;

    /** The viewer as a component. */
    private JComponent component;

    /** The dialog displaying info about the image.*/
    private TinyDialog infoDialog;

    /** The dialog displaying info about the image.*/
    private TinyDialog channelDialog;

    /** The magnification factor for the big image.*/
    private double bigImageMagnification;

    /** Item used to show or hide the unit bar. */
    private JCheckBoxMenuItem unitBarItem;

    /** Item used to show or hide the unit bar. */
    private JMenu scaleBarMenu;

    /**
     * Finds the first {@link HistoryItem} in <code>x</code>'s containment
     * hierarchy.
     * 
     * @param x A component.
     * @return The parent {@link HistoryItem} or <code>null</code> if none
     *         was found.
     */
    private HistoryItem findParentDisplay(Object x) {
        while (true) {
            if (x instanceof HistoryItem)
                return (HistoryItem) x;
            if (x instanceof JComponent)
                x = ((JComponent) x).getParent();
            else
                break;
        }
        return null;
    }

    /**
     * Initializes and returns a split pane, either vertical or horizontal 
     * depending on the passed parameter.
     * 
     * @param orientation The orientation of the split pane.
     * @return See above.
     */
    private JSplitPane initSplitPane(int orientation) {
        int type;
        switch (orientation) {
        case JSplitPane.HORIZONTAL_SPLIT:
        case JSplitPane.VERTICAL_SPLIT:
            type = orientation;
            break;
        default:
            type = JSplitPane.HORIZONTAL_SPLIT;
        }
        JSplitPane pane = new JSplitPane(type);
        pane.setOneTouchExpandable(true);
        pane.setContinuousLayout(true);
        pane.setDividerLocation(-1);
        pane.setResizeWeight(0.0);
        return pane;
    }

    /** Initializes the split panes. */
    private void initSplitPanes() {
        if (historyUI == null)
            historyUI = new HistoryUI(this, model, controller);
        //historySplit = new SplitPanel(SplitPanel.HORIZONTAL);
        historySplit = initSplitPane(JSplitPane.VERTICAL_SPLIT);
        rendererSplit = initSplitPane(JSplitPane.HORIZONTAL_SPLIT);
    }

    /** 
     * Creates the menu bar.
     * 
     * @param pref The user preferences.
     * @return The menu bar. 
     */
    private JMenuBar createMenuBar(ViewerPreferences pref) {
        zoomMenu = new JMenu("Zoom");
        zoomMenu.setMnemonic(KeyEvent.VK_Z);
        zoomingGroup = new ButtonGroup();
        //Create zoom grid menu
        zoomGridMenu = new JMenu("Zoom");
        zoomingGridGroup = new ButtonGroup();

        JMenuBar menuBar = new JMenuBar();
        menuBar.add(createControlsMenu(pref));
        menuBar.add(createViewMenu(pref));
        if (!model.isBigImage())
            menuBar.add(createZoomMenu(pref, true));
        menuBar.add(createShowViewMenu());
        TaskBar tb = ImViewerAgent.getRegistry().getTaskBar();
        //menuBar.add(tb.getWindowsMenu());
        menuBar.add(tb.getMenu(TaskBar.WINDOW_MENU));
        menuBar.add(tb.getMenu(TaskBar.HELP_MENU));
        return menuBar;
    }

    /**
     * Helper method to create the background color sub-menu.
     * 
     * @param pref The user preferences.
     * @return See above.
     */
    private JMenuItem createBackgroundColorSubMenu(ViewerPreferences pref) {
        JMenu menu = new JMenu("Background color");
        bgColorGroup = new ButtonGroup();
        Iterator<Entry<Color, String>> i = backgrounds.entrySet().iterator();
        ColorCheckBoxMenuItem item;
        Color c;
        Color refColor = ImagePaintingFactory.DEFAULT_BACKGROUND;
        if (pref != null)
            refColor = pref.getBackgroundColor();
        if (refColor == null)
            refColor = ImagePaintingFactory.DEFAULT_BACKGROUND;
        Entry<Color, String> entry;
        while (i.hasNext()) {
            entry = i.next();
            c = entry.getKey();
            item = new ColorCheckBoxMenuItem(c);
            item.setText(entry.getValue());
            item.setSelected(c.equals(refColor));
            bgColorGroup.add(item);
            menu.add(item);
            item.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    ColorCheckBoxMenuItem src = (ColorCheckBoxMenuItem) e.getSource();
                    if (src.isSelected()) {
                        controller.setPreferences();
                        if (lens != null)
                            lens.setBackgroundColor(src.getColor());
                        model.getBrowser().setBackgroundColor(src.getColor());
                    }
                }
            });
        }
        return menu;
    }

    /**
     * Helper method to create the unit bar color sub-menu.
     * 
     * @param pref The user preferences.
     * @return See above.
     */
    private JMenuItem createScaleBarColorSubMenu(ViewerPreferences pref) {
        JMenu menu = new JMenu("Scale bar/Text color");
        ButtonGroup group = new ButtonGroup();
        Iterator<Entry<Color, String>> i = EditorUtil.COLORS_BAR.entrySet().iterator();
        ColorCheckBoxMenuItem item;
        Color c;
        Color refColor = ImagePaintingFactory.UNIT_BAR_COLOR;
        if (pref != null)
            refColor = pref.getScaleBarColor();
        if (refColor == null)
            refColor = ImagePaintingFactory.UNIT_BAR_COLOR;
        Entry<Color, String> entry;
        while (i.hasNext()) {
            entry = i.next();
            c = entry.getKey();
            item = new ColorCheckBoxMenuItem(c);
            item.setText(entry.getValue());
            item.setSelected(c.equals(refColor));
            group.add(item);
            menu.add(item);
            item.addActionListener(new ActionListener() {

                public void actionPerformed(ActionEvent e) {
                    ColorCheckBoxMenuItem source = (ColorCheckBoxMenuItem) e.getSource();
                    if (source.isSelected())
                        model.getBrowser().setUnitBarColor(source.getColor());
                }

            });
        }
        return menu;
    }

    /**
     * Helper method to create the unit bar sub-menu.
     * 
     * @param pref The user preferences.
     * @return See above.
     */
    private JMenu createScaleBarLengthSubMenu(ViewerPreferences pref) {
        scaleBarMenu = new JMenu(SCALE_BAR_TEXT + model.getUnits() + ")");
        scaleBarGroup = new ButtonGroup();
        if (pref != null && pref.getScaleBarIndex() > 0)
            defaultIndex = pref.getScaleBarIndex();
        UnitBarSizeAction a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_ONE);
        JCheckBoxMenuItem item = new JCheckBoxMenuItem(a);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarGroup.add(item);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_TWO);
        item = new JCheckBoxMenuItem(a);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarGroup.add(item);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_FIVE);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_FIVE));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_TEN);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_TEN));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_TWENTY);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_TWENTY));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_FIFTY);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_FIFTY));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_HUNDRED);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_HUNDRED));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        a = (UnitBarSizeAction) controller.getAction(ImViewerControl.UNIT_BAR_CUSTOM);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.UNIT_BAR_CUSTOM));
        scaleBarGroup.add(item);
        item.setSelected(a.getIndex() == defaultIndex);
        scaleBarMenu.add(item);
        return scaleBarMenu;
    }

    /**
     * Helper method to create the view menu.
     * 
     * @param pref The user preferences.
     * @return The controls sub-menu.
     */
    private JMenu createViewMenu(ViewerPreferences pref) {
        JMenu menu = new JMenu("Display");
        menu.setMnemonic(KeyEvent.VK_V);
        unitBarItem = new JCheckBoxMenuItem();
        unitBarItem.setSelected(model.isUnitBar());
        unitBarItem.setAction(controller.getAction(ImViewerControl.UNIT_BAR));
        menu.add(unitBarItem);
        menu.add(createScaleBarLengthSubMenu(pref));
        menu.add(createScaleBarColorSubMenu(pref));
        menu.add(new JSeparator(JSeparator.HORIZONTAL));
        menu.add(createBackgroundColorSubMenu(pref));
        //menu.add(new JSeparator(JSeparator.HORIZONTAL));
        return menu;
    }

    /** Synchronizes the unit bar selection. */
    void handleUnitBar() {
        unitBarItem.removeActionListener(controller.getAction(ImViewerControl.UNIT_BAR));
        unitBarItem.setSelected(model.isUnitBar());
        unitBarItem.setAction(controller.getAction(ImViewerControl.UNIT_BAR));
        scaleBarMenu.setText(SCALE_BAR_TEXT + model.getUnits() + ")");
    }

    /**
     * Helper method to create the controls menu.
     * 
     * @param pref The user preferences.
     * @return The controls sub-menu.
     */
    private JMenu createControlsMenu(ViewerPreferences pref) {
        JMenu menu = new JMenu("Controls");
        menu.setMnemonic(KeyEvent.VK_C);
        ViewerAction action = controller.getAction(ImViewerControl.RENDERER);

        rndItem = new JCheckBoxMenuItem();
        rndItem.setSelected(isRendererShown());
        rndItem.setAction(action);
        rndItem.setText(action.getName());
        if (pref != null)
            rndItem.setSelected(pref.isRenderer());
        //menu.add(rndItem);

        action = controller.getAction(ImViewerControl.METADATA);
        metadataItem = new JCheckBoxMenuItem();
        metadataItem.setSelected(isRendererShown());
        metadataItem.setAction(action);
        metadataItem.setText(action.getName());
        if (pref != null)
            metadataItem.setSelected(pref.isRenderer());
        //menu.add(metadataItem);

        action = controller.getAction(ImViewerControl.HISTORY);
        historyItem = new JCheckBoxMenuItem();
        historyItem.setSelected(isHistoryShown());
        historyItem.setAction(action);
        historyItem.setText(action.getName());
        if (pref != null)
            historyItem.setSelected(pref.isHistory());
        //menu.add(historyItem);

        action = controller.getAction(ImViewerControl.MOVIE);
        JMenuItem item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);
        action = controller.getAction(ImViewerControl.LENS);
        item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);
        action = controller.getAction(ImViewerControl.MEASUREMENT_TOOL);
        item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);
        menu.add(new JSeparator(JSeparator.HORIZONTAL));
        //Color model
        colorModelGroup = new ButtonGroup();
        action = controller.getAction(ImViewerControl.GREY_SCALE_MODEL);
        item = new JCheckBoxMenuItem();
        String cm = model.getColorModel();
        item.setSelected(cm.equals(ImViewer.GREY_SCALE_MODEL));
        item.setAction(action);
        colorModelGroup.add(item);
        menu.add(item);
        action = controller.getAction(ImViewerControl.RGB_MODEL);
        item = new JCheckBoxMenuItem();
        item.setAction(action);
        item.setSelected(cm.equals(ImViewer.RGB_MODEL));
        colorModelGroup.add(item);
        menu.add(item);

        menu.add(new JSeparator());
        action = controller.getAction(ImViewerControl.CHANNELS_ON);
        item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);
        action = controller.getAction(ImViewerControl.CHANNELS_OFF);
        item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);

        menu.add(new JSeparator());
        action = controller.getAction(ImViewerControl.SAVE);
        item = new JMenuItem(action);
        item.setText(action.getName());
        menu.add(item);
        action = controller.getAction(ImViewerControl.PREFERENCES);
        item = new JMenuItem(action);
        item.setText(action.getName());
        //menu.add(item);
        return menu;
    }

    /**
     * Helper method to create the Zoom menu. 
     * 
     * @param pref  The user preferences.
     * @param full  Pass <code>true</code> to create a full menu, 
     *             <code>false</code> to create a partial menu.
     * @return See above.
     */
    private JMenu createZoomMenu(ViewerPreferences pref, boolean full) {
        ViewerAction action;
        JCheckBoxMenuItem item;
        if (model.isBigImage()) {
            action = controller.getAction(ImViewerControl.ZOOM_100);
            item = new JCheckBoxMenuItem();
            item.setAction(action);
            zoomMenu.add(item);
            action = controller.getAction(ImViewerControl.ZOOM_125);
            item = new JCheckBoxMenuItem();
            item.setAction(action);
            zoomMenu.add(item);
            action = controller.getAction(ImViewerControl.ZOOM_150);
            item = new JCheckBoxMenuItem();
            item.setAction(action);
            zoomMenu.add(item);
            action = controller.getAction(ImViewerControl.ZOOM_175);
            item = new JCheckBoxMenuItem();
            item.setAction(action);
            zoomMenu.add(item);
            return zoomMenu;
        }
        action = controller.getAction(ImViewerControl.ZOOM_25);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_50);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_75);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_100);
        item = new JCheckBoxMenuItem();
        item.setAction(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        //if (full) {
        action = controller.getAction(ImViewerControl.ZOOM_125);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_150);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_175);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_200);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_225);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_250);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_275);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        action = controller.getAction(ImViewerControl.ZOOM_300);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        //}

        action = controller.getAction(ImViewerControl.ZOOM_FIT_TO_WINDOW);
        item = new JCheckBoxMenuItem(action);
        zoomMenu.add(item);
        zoomingGroup.add(item);
        int index = ZoomAction.DEFAULT_ZOOM_INDEX;
        double factor = ZoomAction.DEFAULT_ZOOM_FACTOR;
        if (pref != null) {
            if (pref.isFieldSelected(ViewerPreferences.ZOOM_FACTOR)) {
                index = pref.getZoomIndex();
                factor = ZoomAction.getZoomFactor(index);
            }
        }
        setZoomFactor(factor, index);

        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.ZOOM_GRID_25));
        zoomGridMenu.add(item);
        zoomingGridGroup.add(item);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.ZOOM_GRID_50));
        zoomGridMenu.add(item);
        zoomingGridGroup.add(item);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.ZOOM_GRID_75));
        zoomGridMenu.add(item);
        zoomingGridGroup.add(item);
        item = new JCheckBoxMenuItem(controller.getAction(ImViewerControl.ZOOM_GRID_100));
        zoomGridMenu.add(item);
        zoomingGridGroup.add(item);
        setGridMagnificationFactor(ZoomGridAction.DEFAULT_ZOOM_FACTOR);
        return zoomMenu;
    }

    /**
     * Helper method to create the show View menu. 
     * 
     * @return See above.
     */
    private JMenu createShowViewMenu() {
        JMenu menu = new JMenu("View");
        menu.setMnemonic(KeyEvent.VK_S);
        JMenuItem item = new JMenuItem(controller.getAction(ImViewerControl.TAB_GRID));
        if (model.isBigImage() || (model.isLifetimeImage() && model.getModuloT() == null))
            item.setEnabled(false);
        else
            item.setEnabled(model.getMaxC() > 1);
        menu.add(item);
        item = new JMenuItem(controller.getAction(ImViewerControl.TAB_PROJECTION));
        item.setEnabled(model.getMaxZ() > 1 && !model.isBigImage());
        menu.add(item);
        return menu;
    }

    /** Builds and lays out the GUI. */
    private void buildGUI() {
        Browser browser = model.getBrowser();
        int sizeX = model.getTiledImageSizeX();
        int sizeY = model.getTiledImageSizeY();

        browser.setComponentsSize(sizeX, sizeY);
        tabs = new ClosableTabbedPane(JTabbedPane.TOP, JTabbedPane.WRAP_TAB_LAYOUT);
        tabs.setAlignmentX(LEFT_ALIGNMENT);

        viewPanel = new ClosableTabbedPaneComponent(ImViewer.VIEW_INDEX, browser.getTitle(), browser.getIcon(), "");
        viewPanel.setClosable(false);
        double[][] tl = { { TableLayout.PREFERRED, TableLayout.FILL },
                { TableLayout.FILL, TableLayout.PREFERRED, TableLayout.PREFERRED } };
        viewPanel.setLayout(new TableLayout(tl));
        viewPanel.add(controlPane, "0, 0");
        viewPanel.add(browser.getUI(), "1, 0");
        viewPanel.add(controlPane.getTimeSliderPane(ImViewer.VIEW_INDEX), "1, 1");
        if (model.isLifetimeImage()) {
            viewPanel.add(controlPane.getLifetimeSliderPane(ImViewer.VIEW_INDEX), "1, 2");
        }
        tabbedIconHeight = browser.getIcon().getIconHeight() + ICON_EXTRA;

        tabs.insertTab(browser.getTitle(), browser.getIcon(), viewPanel, "", ImViewer.VIEW_INDEX);
        gridViewPanel = new ClosableTabbedPaneComponent(ImViewer.GRID_INDEX, browser.getGridViewTitle(),
                browser.getGridViewIcon(), "");
        gridViewPanel.setLayout(new TableLayout(tl));

        gridViewPanel.add(controlPane.buildGridComponent(), "0, 0");
        gridViewPanel.add(browser.getGridView(), "1, 0");
        gridViewPanel.add(controlPane.getTimeSliderPane(ImViewer.GRID_INDEX), "1, 1");
        if (model.isLifetimeImage()) {
            gridViewPanel.add(controlPane.getLifetimeSliderPane(ImViewer.GRID_INDEX), "1, 2");
        }
        if (model.allowSplitView() && !model.isBigImage()) {
            tabs.insertTab(browser.getGridViewTitle(), browser.getGridViewIcon(), gridViewPanel, "",
                    ImViewer.GRID_INDEX);
        }

        double[][] tl2 = { { TableLayout.PREFERRED, TableLayout.FILL },
                { TableLayout.PREFERRED, TableLayout.FILL, TableLayout.PREFERRED } };

        projectionViewPanel = new ClosableTabbedPaneComponent(ImViewer.PROJECTION_INDEX,
                browser.getProjectionViewTitle(), browser.getProjectionViewIcon(), "");

        projectionViewPanel.setLayout(new TableLayout(tl2));
        projectionViewPanel.add(controlPane.buildProjectionToolBar(), "0, 0, 1, 0");
        projectionViewPanel.add(controlPane.buildProjectionComponent(), "0, 1");
        projectionViewPanel.add(browser.getProjectionView(), "1, 1");
        projectionViewPanel.add(controlPane.getTimeSliderPane(ImViewer.PROJECTION_INDEX), "1, 2");
        if (model.getMaxZ() > 0 && !model.isBigImage()) {
            tabs.insertTab(browser.getProjectionViewTitle(), browser.getProjectionViewIcon(), projectionViewPanel,
                    "", ImViewer.PROJECTION_INDEX);
        }

        tabs.addChangeListener(controller);

        //mainComponent = tabs;
        rendererSplit.setLeftComponent(tabs);
        mainComponent = rendererSplit;

        Container container = getContentPane();
        container.setLayout(new BorderLayout(0, 0));
        container.add(toolBar, BorderLayout.NORTH);
        container.add(mainComponent, BorderLayout.CENTER);
        container.add(statusBar, BorderLayout.SOUTH);

        //attach listener to the frame border
        boundsAdapter = new HierarchyBoundsAdapter() {

            /**
             * Stores the size of the tab pane when the frame is resized.
             * @see HierarchyBoundsListener#ancestorResized(HierarchyEvent)
             */
            public void ancestorResized(HierarchyEvent e) {
                if (tabs != null)
                    restoreSize = tabs.getSize();
            }
        };
        container.addHierarchyBoundsListener(boundsAdapter);
        //restoreSize = new Dimension(0, 0);
        //layoutComponents(false);
    }

    /**
     * Returns the size this widget should have to display the image
     * before adding the split panes.
     * 
     * @param w The width of the component added to the center of the 
     *          container.
     * @param h   The height of the component added to the center of the 
     *          container.
     * @return See above.
     */
    private Dimension getIdealSize(int w, int h) {
        Dimension sz = new Dimension();
        Dimension tbDim = toolBar.getPreferredSize();
        Dimension statusDim = statusBar.getPreferredSize();
        Insets frameInsets = getInsets();
        Insets stInsets = statusBar.getInsets();
        sz.width = w + frameInsets.left + frameInsets.right + stInsets.left + stInsets.right
                + rendererSplit.getDividerSize();
        sz.height = h + tbDim.height + statusDim.height + frameInsets.top + frameInsets.bottom + tabbedIconHeight
                + stInsets.top + stInsets.bottom;
        return sz;
    }

    /**
     * Adds a left and right component to the specified pane.
     * 
     * @param pane   The split pane to add the components to.
     * @param left   The left component to add.
     * @param right The right component to add.
     */
    private void addComponents(JSplitPane pane, JComponent left, JComponent right) {
        Component c = pane.getLeftComponent();
        if (c != null)
            pane.remove(c);
        c = pane.getRightComponent();
        if (c != null)
            pane.remove(c);
        pane.setLeftComponent(left);
        pane.setRightComponent(right);
    }

    /** 
     * Lays out the components composing main panel. 
     * 
     * @param fromPreferences   Pass <code>true</code> to indicate that the 
     *                      method is invoked while setting the user 
     *                      preferences, <code>false</code> otherwise.
     */
    private void layoutComponents(boolean fromPreferences) {
        //initSplitPanes();
        Dimension d;
        int diff;
        Container container = getContentPane();
        container.removeHierarchyBoundsListener(boundsAdapter);
        int width = 0, height = 0;
        JComponent rightComponent;
        //int divider = 0;
        int vExtra = 2;
        int addition;
        switch (displayMode) {
        case RENDERER:
            rightComponent = model.getMetadataViewer().getEditorUI();
            rendererSplit.setRightComponent(rightComponent);
            if (restoreSize == null) {
                rendererSplit.setResizeWeight(1.0);
                return;
            }
            d = model.getMetadataViewer().getIdealRendererSize();
            rightComponent.setMinimumSize(d);
            tabs.setMinimumSize(restoreSize);
            height = restoreSize.height;
            diff = d.height - restoreSize.height;
            if (diff > 0)
                height += diff;
            else
                height += vExtra;
            addition = rendererSplit.getDividerSize() + 2 * (refInsets.left + refInsets.right);

            width = restoreSize.width + d.width;
            width += 4 * addition;
            break;
        case HISTORY:
            container.remove(mainComponent);
            historyUI.doGridLayout();
            addComponents(historySplit, tabs, historyUI);
            mainComponent = historySplit;
            container.add(mainComponent, BorderLayout.CENTER);
            container.validate();
            container.repaint();
            height = restoreSize.height;
            width = restoreSize.width;
            d = historyUI.getIdealSize();
            addition = historySplit.getDividerSize() + 2 * (refInsets.top + refInsets.bottom);
            height += d.height;
            historySplit.setResizeWeight(0.49);
            height += addition;
            break;
        case HISTORY_AND_RENDERER:
            historySplit.setResizeWeight(0.49);
            container.remove(mainComponent);
            historyUI.doGridLayout();
            rightComponent = model.getMetadataViewer().getEditorUI();
            addComponents(rendererSplit, tabs, rightComponent);
            addComponents(historySplit, rendererSplit, historyUI);
            mainComponent = historySplit;
            container.add(mainComponent, BorderLayout.CENTER);
            container.validate();
            container.repaint();

            d = model.getMetadataViewer().getIdealRendererSize();
            height = restoreSize.height;
            diff = d.height - restoreSize.height;
            if (diff > 0)
                height += diff;
            else
                height += vExtra;
            addition = rendererSplit.getDividerSize() + 2 * (refInsets.left + refInsets.right);

            width = restoreSize.width + d.width;
            width += 4 * addition;
            d = historyUI.getPreferredSize();
            addition = historySplit.getDividerSize() + 2 * (refInsets.top + refInsets.bottom);
            height += d.height;
            height += addition;
            break;
        case NEUTRAL:
            rightComponent = model.getMetadataViewer().getEditorUI();
            rendererSplit.remove(rightComponent);
            if (restoreSize == null)
                return;
            width = restoreSize.width;
            height = restoreSize.height;
            break;
        default:
        }
        //rendererSplit.setDividerLocation(-1);
        //rendererSplit.setResizeWeight(1.0);
        //historySplit.setDividerLocation(-1);
        d = getIdealSize(width, height);

        /* Need to review that code.
         * Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
        int w = (int) (screen.width*SCREEN_RATIO);
        int h = (int) (screen.height*SCREEN_RATIO);
        if (d.width > w || d.height > h) {
           setSize(width, height);
        } else setSize(d);
        */
        setSize(d);
        setPreferredSize(d);
        pack();
        container.addHierarchyBoundsListener(boundsAdapter);
    }

    /** Packs the window and resizes it if the screen is too small. */
    private void packWindow() {
        pack();
        Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
        Dimension size = getSize();
        int width = (int) (screenSize.width * SCREEN_RATIO);
        int height = (int) (screenSize.height * SCREEN_RATIO);
        int w = size.width;
        int h = size.height;
        boolean reset = false;
        if (w > width) {
            reset = true;
        }
        if (h > height) {
            reset = true;
            h = height;
        }
        if (reset) {
            setSize(w, h);
        }
    }

    /**
     * Removes all the elements from the passed menu and button group.
     * 
     * @param group The group to handle.
     * @param menu  The menu to handle.
     */
    private void clearZoomMenu(ButtonGroup group, JMenu menu) {
        menu.removeAll();
        for (Enumeration<AbstractButton> e = group.getElements(); e.hasMoreElements();)
            group.remove(e.nextElement());
    }

    /**
     * Creates a new instance.
     * The {@link #initialize(ImViewerControl, ImViewerModel) initialize} 
     * method should be called straight after to link this View 
     * to the Controller.
     * 
     * @param title The window title.
     */
    ImViewerUI(String title) {
        super(title);
        loadingWindow = new LoadingWindow(this);
        loadingWindow.setTitle("Opening Image Viewer...");
        loadingWindow.setModal(false);
        defaultIndex = UnitBarSizeAction.DEFAULT_UNIT_INDEX;
        displayMode = NEUTRAL;
        bigImageMagnification = 1.0;
    }

    /**
     * Links this View to its Controller and Model.
     * 
     * @param controller    Reference to the Control.
     *                      Mustn't be <code>null</code>.
     * @param model         Reference to the Model.
     *                      Mustn't be <code>null</code>.
     */
    void initialize(ImViewerControl controller, ImViewerModel model) {
        if (controller == null)
            throw new NullPointerException("No control.");
        if (model == null)
            throw new NullPointerException("No model.");
        this.controller = controller;
        this.model = model;
        toolBar = new ToolBar(this, controller);
        controlPane = new ControlPane(controller, model, this);
        statusBar = new StatusBar(model, this);
        initSplitPanes();
        refInsets = rendererSplit.getInsets();
        planes = new HashMap<Integer, PlaneInfoComponent>();
        ImageIcon icon = IconManager.getInstance().getImageIcon(IconManager.VIEWER);
        if (icon != null)
            setIconImage(icon.getImage());
        setName("image viewer window");
    }

    /** 
     * This method should be called straight after the metadata and the
     * rendering settings are loaded.
     */
    void buildComponents() {
        //Retrieve the preferences.
        ViewerPreferences pref = ImViewerFactory.getPreferences();
        setJMenuBar(createMenuBar(pref));
        toolBar.buildComponent();
        controlPane.buildComponent();
        buildGUI();
        List<ChannelData> data = model.getChannelData();
        ChannelData d;
        int index;
        PlaneInfoComponent comp;
        Iterator<ChannelData> i = data.iterator();
        while (i.hasNext()) {
            d = i.next();
            index = d.getIndex();
            comp = new PlaneInfoComponent(model.getChannelColor(index));
            comp.addPropertyChangeListener(PlaneInfoComponent.PLANE_INFO_PROPERTY, controller);
            planes.put(index, comp);
        }
    }

    /**
     * Updates UI components when a zooming factor is selected.
     * 
     * @param factor   The magnification factor.
     * @param zoomIndex The index of the selected zoomFactor.
     */
    void setZoomFactor(double factor, int zoomIndex) {
        setMagnificationStatus(factor, zoomIndex);
        JCheckBoxMenuItem b;
        Enumeration e;
        Action a;
        if (zoomingGroup == null)
            return;
        for (e = zoomingGroup.getElements(); e.hasMoreElements();) {
            b = (JCheckBoxMenuItem) e.nextElement();
            a = b.getAction();
            if (a instanceof ZoomAction) {
                b.removeActionListener(a);
                b.setSelected(((ZoomAction) a).getIndex() == zoomIndex);
                b.setAction(a);
            }
        }
        controlPane.setZoomFactor(zoomIndex);
    }

    /**
     * Sets the magnification value in the status bar depending on the
     * selected tabbedPane.
     * 
     * @param factor The value to set.
     * @param zoomIndex The index of the selected zoomFactor.
     */
    void setMagnificationStatus(double factor, int zoomIndex) {
        if (statusBar == null)
            return;
        if (factor != ZoomAction.ZOOM_FIT_FACTOR)
            statusBar.setRigthStatus(Math.round(factor * model.getOriginalRatio() * 100) + "%");
        else
            statusBar.setRigthStatus(ZoomAction.ZOOM_FIT_NAME);
        if (model.isBigImage()) {
            ResolutionLevel level = model.getResolutionDescription();
            double f = UIUtilities.roundTwoDecimals(level.getRatio() * 100);
            bigImageMagnification = level.getRatio();
            statusBar.setRigthStatus(f + "%");
        }
    }

    /**
     * Returns the magnification factor for big images.
     * 
     * @return See above.
     */
    double getBigImageMagnificationFactor() {
        if (!model.isBigImage())
            return 1.0;
        return bigImageMagnification;
    }

    /**
     * Returns the index associated to the zoom factor.
     * 
     * @return See above.
     */
    int getZoomIndex() {
        if (zoomingGroup == null)
            return ZoomAction.ZOOM_FIT_TO_WINDOW;
        JCheckBoxMenuItem b;
        Enumeration e;
        Action a;
        for (e = zoomingGroup.getElements(); e.hasMoreElements();) {
            b = (JCheckBoxMenuItem) e.nextElement();
            a = b.getAction();
            if (b.isSelected())
                return ((ZoomAction) a).getIndex();
        }
        return ZoomAction.ZOOM_FIT_TO_WINDOW;
    }

    /**
     * Updates UI components when a new color model is selected.
     * 
     * @param key The index of the color model.
     */
    void setColorModel(int key) {
        controlPane.setColorModel();
        AbstractButton b;
        Action a;
        Enumeration<AbstractButton> e;
        for (e = colorModelGroup.getElements(); e.hasMoreElements();) {
            b = e.nextElement();
            a = b.getAction();
            if (a instanceof ColorModelAction) {
                b.removeActionListener(a);
                b.setSelected(((ColorModelAction) a).getIndex() == key);
                b.setAction(a);
            }
        }
    }

    /**
     * Updates UI components when a new z-section is selected.
     * 
     * @param z The selected z-section.
     */
    void setZSection(int z) {
        controlPane.setZSection(z);
    }

    /**
     * Updates UI components when a new timepoint is selected.
     * 
     * @param t The selected timepoint.
     */
    void setTimepoint(int t) {
        controlPane.setTimepoint(t);
    }

    /**
     * Updates UI components when a new bin is selected.
     * 
     * @param bin The selected bin.
     */
    void setBin(int t) {
        controlPane.setBin(t);
    }

    /**
     * Returns the {@link #loadingWindow}.
     * 
     * @return See above.
     */
    LoadingWindow getLoadingWindow() {
        return loadingWindow;
    }

    /** 
     * Reacts to {@link ImViewer} change events.
     * 
     * @param b Pass <code>true</code> to enable the UI components, 
     *          <code>false</code> otherwise.
     */
    void onStateChange(boolean b) {
        model.getBrowser().onStateChange(b);
        if (tabs != null) {
            tabs.setEnabled(b);
            enableSliders(b);
            controlPane.onStateChange(b);
            toolBar.onStateChange(b);
        }
    }

    /** Sets the default text of the status bar. */
    void setLeftStatus() {
        int n;
        int max = model.getMaxZ();
        double d = model.getPixelsSizeZ();
        UnitsObject o;
        String units;
        StringBuffer buffer = new StringBuffer();
        if (model.getTabbedIndex() == ImViewer.PROJECTION_INDEX) {
            n = getProjectionStartZ();
            int m = getProjectionEndZ();
            buffer.append("Z range:" + (n + 1));
            buffer.append("-" + (getProjectionEndZ() + 1));
            if (d > 0 && max > 0) {
                o = EditorUtil.transformSize(n * d);
                buffer.append(" (" + UIUtilities.roundTwoDecimals(o.getValue()));
                buffer.append("-");
                o = EditorUtil.transformSize(m * d);
                units = o.getUnits();
                buffer.append(UIUtilities.roundTwoDecimals(o.getValue()));
                buffer.append(units + ")");
            }
            buffer.append("/" + (model.getMaxZ() + 1));
            controlPane.setRangeSliderToolTip(n, m);
        } else {
            n = model.getDefaultZ();
            buffer.append("Z=" + (n + 1));
            if (d > 0 && max > 0) {
                o = EditorUtil.transformSize(n * d);
                units = o.getUnits();
                buffer.append(" (" + UIUtilities.roundTwoDecimals(o.getValue()));
                buffer.append(units + ")");
            }

            buffer.append("/" + (model.getMaxZ() + 1));
        }
        buffer.append(" T=" + (model.getRealSelectedT() + 1) + "/" + model.getRealT());
        if (model.isLifetimeImage()) {
            int bin = model.getSelectedBin();
            buffer.append(" ");
            buffer.append(EditorUtil.SMALL_T_VARIABLE + "=" + (bin + 1));
            buffer.append("/" + (model.getMaxLifetimeBin()));
            //format the result
            ModuloInfo info = model.getModuloT();
            if (info != null) {
                buffer.append(" (");
                buffer.append(UIUtilities.roundTwoDecimals(info.getRealValue(bin)));
                buffer.append(info.getUnit() + ")");
            }
        }
        setLeftStatus(buffer.toString());
    }

    /**
     * Updates status bar.
     * 
     * @param description   The text to display.
     */
    void setLeftStatus(String description) {
        statusBar.setLeftStatus(description);
    }

    /**
     * Displays the plane information.
     * 
     */
    void setPlaneInfoStatus() {
        if (model.getTabbedIndex() == ImViewer.PROJECTION_INDEX) {
            statusBar.setCenterStatus(new JLabel());
            return;
        }
        List<Integer> indexes = model.getActiveChannels();
        if (CollectionUtils.isEmpty(indexes)) {
            statusBar.setCenterStatus(new JLabel());
            return;
        }

        int z = model.getDefaultZ();
        int t = model.getRealSelectedT();
        PlaneInfo info;
        String s, toolTipText;
        Map<Integer, Color> colors = model.getChannelsColorMap();
        JPanel panel = new JPanel();
        panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
        Map<String, Object> details;
        List<String> tips;
        PlaneInfoComponent comp;

        List<ChannelData> metadata = model.getChannelData();
        Iterator<ChannelData> c = metadata.iterator();
        int index;
        List<String> notSet;
        while (c.hasNext()) {
            index = c.next().getIndex();
            s = "";
            toolTipText = "";
            tips = new ArrayList<String>();
            info = model.getPlane(z, index, t);
            comp = planes.get(index);
            if (info != null) {
                details = EditorUtil.transformPlaneInfo(info);
                notSet = (List<String>) details.get(EditorUtil.NOT_SET);
                comp.setColor(colors.get(index));
                if (!notSet.contains(EditorUtil.DELTA_T)) {
                    s += EditorUtil.formatTimeInSeconds((Double) details.get(EditorUtil.DELTA_T));
                }
                if (!notSet.contains(EditorUtil.EXPOSURE_TIME)) {
                    toolTipText += EditorUtil.EXPOSURE_TIME + ": ";
                    toolTipText += details.get(EditorUtil.EXPOSURE_TIME);
                    toolTipText += EditorUtil.TIME_UNIT;
                    tips.add(toolTipText);
                }
                toolTipText = "";
                toolTipText += "Stage coordinates: ";
                if (!notSet.contains(EditorUtil.POSITION_X))
                    toolTipText += "x=" + details.get(EditorUtil.POSITION_X) + " ";
                if (!notSet.contains(EditorUtil.POSITION_Y))
                    toolTipText += "y=" + details.get(EditorUtil.POSITION_Y) + " ";
                if (!notSet.contains(EditorUtil.POSITION_Z))
                    toolTipText += "z=" + details.get(EditorUtil.POSITION_Z);
                tips.add(toolTipText);
                comp.setToolTipText(UIUtilities.formatToolTipText(tips));
                if (StringUtils.isEmpty(s))
                    s = "0s";
                comp.setText(s);
                panel.add(comp);
            }
        }
        statusBar.setCenterStatus(panel);
    }

    /**
     * Updates the buttons' selection when a new button is selected or 
     * deselected.
     * 
     * @param index One of the following constants {@link #GRID_ONLY},
     *             {@link #VIEW_ONLY}, {@link #PROJECTION_ONLY} 
     *             and {@link #ALL_VIEW}.
     */
    void setChannelsSelection(int index) {
        controlPane.setChannelsSelection(index);
    }

    /**
     * Sets the active channels in the grid view.
     * 
     * @param channels The collection of channel indexes.
     */
    void setChannelsSelection(List channels) {
        controlPane.setChannelsSelection(channels);
    }

    /** 
     * Sets whether or not the tabbed pane is enabled.
     * 
     * @param enabled    Pass <code>true</code> to enable the component, 
     *                <code>false</code> otherwise.
     */
    void playChannelMovie(boolean enabled) {
        tabs.setEnabled(enabled);
    }

    /** 
     * Sets the color of the specified channel. 
     * 
     * @param index The channel index. 
     * @param c     The color to set.
     */
    void setChannelColor(int index, Color c) {
        PlaneInfoComponent comp = planes.get(index);
        if (comp != null)
            comp.setColor(c);
        controlPane.setChannelColor(index, c);
    }

    /** Resets the defaults. */
    void resetDefaults() {
        controlPane.resetRndSettings();
    }

    /** Resets the UI when switching to a new rendering control. */
    void switchRndControl() {
        controlPane.switchRndControl();
    }

    /** Refreshes the view. */
    void refresh() {
        resetDefaults();
        model.refresh();
    }

    /**
     * Sets the image in the lens to the plane image shown on the screen
     * depending on the selected tab pane.
     */
    void setLensPlaneImage() {
        if (lens == null)
            return;
        switch (model.getTabbedIndex()) {
        case ImViewer.VIEW_INDEX:
            lens.setPlaneImage(model.getOriginalImage());
            break;
        case ImViewer.GRID_INDEX:
            lens.setPlaneImage(model.getGridImage());
            break;
        case ImViewer.PROJECTION_INDEX:
            BufferedImage image = model.getProjectedImage();
            if (image != null)
                lens.setPlaneImage(image);
        }
    }

    /**
     * Creates a zoomed version of the passed image.
     * 
     * @param image The image to zoom.
     * @return See above.
     * @throws Exception 
     */
    BufferedImage createZoomedLensImage(BufferedImage image) {
        if (lens == null)
            return null;
        try {
            return lens.createZoomedImage(image);
        } catch (Exception e) {
            return null;
        }
    }

    /**
     * Returns <code>true</code> if the lens is visible, <code>false</code>
     * otherwise.
     * 
     * @return see above.
     */
    boolean isLensVisible() {
        if (lens != null)
            return lens.isVisible();
        return false;
    }

    /**
     * Returns <code>true</code> if the lens exists, <code>otherwise</code>.
     * 
     * @return See above
     */
    boolean hasLensImage() {
        return (lens != null);
    }

    /**
     * Sets the lens's visibility. If the lens hasn't previously created, 
     * we first create the lens.
     * 
     * @param b          Pass <code>true</code> to display the lens, 
     *                   <code>false</code> otherwise.
     * @param historyIndex   The index of the tab pane. 
     */
    void setLensVisible(boolean b, int historyIndex) {
        boolean firstTime = false;
        if (lens == null) {
            if (b) {
                firstTime = true;
                lens = new LensComponent(this, ImViewerAgent.hasOpenGLSupport());
                lens.setImageName(model.getImageName());
                lens.setXYPixelMicron(model.getPixelsSizeX(), model.getPixelsSizeY());
                lens.addPropertyChangeListener(LensComponent.LENS_LOCATION_PROPERTY, controller);
            } else
                return;

        } else {
            Browser browser = model.getBrowser();
            JComponent c = lens.getLensUI();
            browser.removeComponent(c, ImViewer.VIEW_INDEX);
            browser.removeComponent(c, ImViewer.GRID_INDEX);
            browser.removeComponent(c, ImViewer.PROJECTION_INDEX);
        }
        if (!b) {
            lens.setVisible(b);
            repaint();
            return;
        }
        //depending on the previous selected tabbed pane, 
        //we reset the location of the lens

        int maxX = model.getMaxX();
        int maxY = model.getMaxY();
        float f = 1.0f;
        //BufferedImage img;
        int index = model.getTabbedIndex();

        JComponent c = lens.getLensUI();
        int width = c.getWidth();
        int height = c.getHeight();
        Point p = lens.getLensLocation();
        int lensX = p.x;
        int lensY = p.y;
        if (maxX < width || maxY < height)
            return;
        if (firstTime) {
            //firstTimeLensShown = false;
            int diffX = maxX - width;
            int diffY = maxY - height;
            lensX = diffX / 2;
            lensY = diffY / 2;
            if (lensX + width > maxX)
                lensX = diffX;
            if (lensY + height > maxY)
                lensY = diffY;
        } else {
            switch (historyIndex) {
            case ImViewer.GRID_INDEX:
                if (historyIndex != index) {
                    Point point = model.getBrowser().isOnImageInGrid(lens.getLensScaledBounds());
                    if (point == null) {
                        int diffX = maxX - width;
                        int diffY = maxY - height;
                        lensX = diffX / 2;
                        lensY = diffY / 2;
                        if (lensX + width > maxX)
                            lensX = diffX;
                        if (lensY + height > maxY)
                            lensY = diffY;
                    } else {
                        double r = model.getBrowser().getRatio();
                        lensX = (int) (point.x / r);
                        lensY = (int) (point.y / r);
                    }
                }
                break;
            case ImViewer.PROJECTION_INDEX:
            case ImViewer.VIEW_INDEX:
                if (index == ImViewer.GRID_INDEX) {
                    double r = model.getBrowser().getRatio();
                    lensX = (int) (lensX * r);
                    lensY = (int) (lensY * r);
                }
            }
        }
        if (ImViewerAgent.hasOpenGLSupport()) {
            TextureData image;

            switch (index) {
            case ImViewer.VIEW_INDEX:
            default:
                f = (float) model.getZoomFactor();
                image = model.getImageAsTexture();
                //img = model.getOriginalImage();
                break;
            case ImViewer.PROJECTION_INDEX:
                f = (float) model.getZoomFactor();
                image = model.getProjectedImageAsTexture();
                //img = model.getProjectedImage();
                break;
            case ImViewer.GRID_INDEX:
                //img = model.getGridImage();
                image = null;
            }
            if (image != null)
                lens.resetLensAsTexture(image, f, lensX, lensY);
        } else {
            BufferedImage img;
            switch (index) {
            case ImViewer.VIEW_INDEX:
            default:
                f = (float) model.getZoomFactor();
                img = model.getOriginalImage();
                break;
            case ImViewer.PROJECTION_INDEX:
                f = (float) model.getZoomFactor();
                img = model.getProjectedImage();
                break;
            case ImViewer.GRID_INDEX:
                img = model.getGridImage();
            }
            if (img != null)
                lens.resetLens(img, f, lensX, lensY);
        }
        model.getBrowser().addComponent(c, index, true);
        scrollLens();
        UIUtilities.setLocationRelativeTo(this, lens.getZoomWindow());
        lens.setVisible(b);
        repaint();
    }

    /**
     * Returns the <code>zoomedImage</code> from the lens component
     * or <code>null</code> if the lens is <code>null</code>.
     * 
     * @return See above.
     */
    BufferedImage getZoomedLensImage() {
        if (lens == null)
            return null;
        return lens.getZoomedImage();
    }

    /**
     * Sets the lens magnification factor.
     * 
     * @param factor The value to set.
     */
    void setImageZoomFactor(float factor) {
        if (lens == null)
            return;
        lens.setImageZoomFactor(factor);
    }

    /** Hides the lens when the window is iconified. */
    void onIconified() {
        if (lens == null)
            return;
        lens.setVisible(false);
        repaint();
    }

    /**
     * Creates the menus corresponding to the passed id and brings it on screen.
     * 
     * @param menuID    The id of the menu. One out of the following constants:
     *                  {@link ImViewer#COLOR_PICKER_MENU},
     *                  {@link ImViewer#CATEGORY_MENU}.
     */
    void showMenu(int menuID) {
        showMenu(menuID, source, location);
    }

    /**
     * Creates the menus corresponding to the passed id and brings it on screen.
     * 
     * @param menuID    The id of the menu. One out of the following constants:
     *                  {@link ImViewer#COLOR_PICKER_MENU},
     *                  {@link ImViewer#CATEGORY_MENU}.
     * @param source   The component that requested the popup menu.
     * @param location   The point at which to display the menu, relative to the
     *                  <code>component</code>'s coordinates.
     */
    void showMenu(int menuID, Component source, Point location) {
        switch (menuID) {
        case ImViewer.COLOR_PICKER_MENU:
            List<ChannelData> data = model.getChannelData();
            ChannelData d;
            JPopupMenu menu = new JPopupMenu();
            ChannelColorMenuItem item;
            Iterator<ChannelData> i = data.iterator();
            int index;
            while (i.hasNext()) {
                d = i.next();
                index = d.getIndex();
                item = new ChannelColorMenuItem(d.getChannelLabeling(), model.getChannelColor(index), index);
                menu.add(item);
                item.addPropertyChangeListener(controller);
            }
            menu.show(source, location.x, location.y);
        }
    }

    /**
     * Scrolls to display the lens when the user drags the lens.
     * 
     * @param bounds The lens' bounds.
     */
    void scrollToNode(Rectangle bounds) {
        if (lens == null)
            return;
        if (!lens.isVisible())
            return;
        model.getBrowser().scrollTo(bounds, true);
    }

    /** Displays the lens on screen when the image is zoomed. */
    void scrollLens() {
        if (lens == null)
            return;
        model.getBrowser().scrollTo(lens.getLensScaledBounds(), false);
    }

    /**
     * Selects the tab pane specified by the passed index.
     * 
     * @param index The index.
     */
    void selectTabbedPane(int index) {
        switch (index) {
        case ImViewer.VIEW_INDEX:
        case ImViewer.GRID_INDEX:
        case ImViewer.PROJECTION_INDEX:
            tabs.setSelectedIndex(index);
            break;
        default:
            return;
        }
    }

    /** 
     * Sets the selected pane.
     * 
     * @param index The index of the selected tab pane.
     */
    void setSelectedPane(int index) {
        JMenuBar menuBar = getJMenuBar();
        Component[] items = menuBar.getComponents();
        Component item;
        int j = -1;
        for (int i = 0; i < items.length; i++) {
            item = items[i];
            if (item == zoomGridMenu || item == zoomMenu)
                j = i;
        }
        if (j != -1)
            menuBar.remove(j);
        double f;
        switch (index) {
        case ImViewer.GRID_INDEX:
            if (j != -1)
                menuBar.add(zoomGridMenu, j);
            f = model.getBrowser().getGridRatio();
            setMagnificationStatus(f, ZoomAction.getIndex(f));
            break;
        case ImViewer.PROJECTION_INDEX:
        case ImViewer.VIEW_INDEX:
        default:
            if (j != -1)
                menuBar.add(zoomMenu, j);
            f = model.getZoomFactor();
            setMagnificationStatus(f, ZoomAction.getIndex(f));
        }
        int oldIndex = model.getTabbedIndex();
        model.setTabbedIndex(index);
        tabs.removeChangeListener(controller);
        int n = tabs.getTabCount();
        Component c;
        int tabbedIndex;
        for (int i = 0; i < n; i++) {
            c = tabs.getComponentAt(i);
            if (c instanceof ClosableTabbedPaneComponent) {
                tabbedIndex = ((ClosableTabbedPaneComponent) c).getIndex();
                if (tabbedIndex == index)
                    tabs.setSelectedIndex(i);
            }
        }

        tabs.addChangeListener(controller);
        setLeftStatus();
        setPlaneInfoStatus();
        model.getBrowser().setSelectedPane(index);
        setLensVisible(isLensVisible(), oldIndex);
        maximizeWindow();
    }

    /**
     * Returns the color model of the pane currently selected.
     * 
     * @return See above.
     */
    String getSelectedPaneColorModel() {
        return controlPane.getSelectedPaneColorModel();
    }

    /** Centers the image when the user maximized the viewer. */
    void maximizeWindow() {
        JComponent c = model.getBrowser().getUI();
        c.setBounds(c.getBounds());
    }

    /** Invokes when the component is resized.*/
    void onComponentResized() {
        model.getBrowser().onComponentResized();
    }

    /**
     * Sets the <code>enable</code> flag of the slider used to select
     * the current z-section and timepoint.
     * 
     * @param enable Pass <code>true</code> to enable the sliders,
     *          <code>false</code> otherwise.
     */
    void enableSliders(boolean enable) {
        if (model.isPlayingMovie()) {
            switch (controller.getMoviePlayer().getMovieIndex()) {
            case MoviePlayerDialog.ACROSS_Z:
                controlPane.enableZSliders(false);
                controlPane.enableTSliders(true);
                break;
            case MoviePlayerDialog.ACROSS_T:
                controlPane.enableZSliders(true);
                controlPane.enableTSliders(false);
                break;
            case MoviePlayerDialog.ACROSS_ZT:
                controlPane.enableSliders(false);
                break;
            }
        } else {
            controlPane.enableSliders(enable);
        }
    }

    /**
     * Sets the specified channel to active.
     * 
     * @param index   The channel's index.
     * @param uiIndex One of the following constants 
      *               {@link ImViewerUI#GRID_ONLY} and 
     *               {@link ImViewerUI#ALL_VIEW}.
     */
    void setChannelActive(int index, int uiIndex) {
        controlPane.setChannelActive(index, uiIndex);
    }

    /**
     * Returns the collection of the active channels in the grid view.
     * 
     * @return See above.
     */
    List getActiveChannelsInGrid() {
        return controlPane.getActiveChannelsInGrid();
    }

    /** 
     * Adds a new item to the history. 
     * 
     * @param node The node to add.
     */
    void addHistoryItem(HistoryItem node) {
        if (!isHistoryShown() || historyUI == null)
            return;
        historyUI.addHistoryItem(node);
    }

    /**
     * Returns <code>true</code> if the renderer is shown, <code>false</code>
     * otherwise.
     * 
     * @return See above.
     */
    boolean isRendererShown() {
        switch (displayMode) {
        case RENDERER:
        case HISTORY_AND_RENDERER:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns <code>true</code> if the history is shown, <code>false</code>
     * otherwise.
     * 
     * @return See above.
     */
    boolean isHistoryShown() {
        switch (displayMode) {
        case HISTORY:
        case HISTORY_AND_RENDERER:
            return true;
        default:
            return false;
        }
    }

    /**
     * Returns the {@link #restoreSize}.
     * 
     * @return See above.
     */
    Dimension geRestoreSize() {
        return restoreSize;
    }

    /** 
     * Shows or hides the local history.
     * 
     * @param b Pass <code>true</code> to display the history,
     *          <code>false</code> otherwise.
     */
    void showHistory(boolean b) {
        boolean rnd = isRendererShown();
        if (b) {
            if (rnd)
                displayMode = HISTORY_AND_RENDERER;
            else
                displayMode = HISTORY;
        } else {
            if (rnd)
                displayMode = RENDERER;
            else
                displayMode = NEUTRAL;
        }
        historyItem.setSelected(isHistoryShown());
        toolBar.displayHistory();
        layoutComponents(false);
    }

    /** 
     * Shows or hides the renderer. 
     * 
     * @param fromPreferences   Pass <code>true</code> to indicate that the 
     *                      method is invoked while setting the user 
     *                      preferences, <code>false</code> otherwise.
     * @param index             The index of the tab to select.
     */
    void showRenderer(boolean fromPreferences, int index) {
        boolean show = !isRendererShown();
        boolean b = isHistoryShown();
        if (show) {
            if (b)
                displayMode = HISTORY_AND_RENDERER;
            else
                displayMode = RENDERER;
        } else {
            if (b)
                displayMode = HISTORY;
            else
                displayMode = NEUTRAL;
        }
        metadataItem.setSelected(isRendererShown());
        rndItem.setSelected(isRendererShown());
        toolBar.displayRenderer();
        layoutComponents(fromPreferences);
        if (show) {
            int v = MetadataViewer.RENDERER_TAB;
            if (index == ImViewer.METADATA_INDEX)
                v = MetadataViewer.GENERAL_TAB;
            model.getMetadataViewer().setSelectedTab(v);
        }
    }

    /**
     * Sets the compression flag.
     * 
     * @param compressionLevel    One of the compression constants defined
     *                      by the model.
     */
    void setCompressionLevel(int compressionLevel) {
        int oldCompression = convertCompressionLevel();
        switch (compressionLevel) {
        case ToolBar.UNCOMPRESSED:
            model.setCompressionLevel(ImViewer.UNCOMPRESSED);
            if (lens != null)
                lens.resetDataBuffered();
            break;
        case ToolBar.MEDIUM:
            if (lens != null && oldCompression == ToolBar.UNCOMPRESSED)
                lens.resetDataBuffered();
            model.setCompressionLevel(ImViewer.MEDIUM);
            break;
        case ToolBar.LOW:
            if (lens != null && oldCompression == ToolBar.UNCOMPRESSED)
                lens.resetDataBuffered();
            model.setCompressionLevel(ImViewer.LOW);
        }
    }

    /** 
     * Returns the UI index corresponding to the current compression level.
     * 
     * @return See above.
     */
    int convertCompressionLevel() {
        return convertCompressionLevel(model.getCompressionLevel());
    }

    /** 
     * Returns the UI index corresponding to the current compression level.
     * 
     * @param level The value to check.
     * @return See above.
     */
    int convertCompressionLevel(int level) {
        switch (level) {
        default:
        case ImViewer.UNCOMPRESSED:
            return ToolBar.UNCOMPRESSED;
        case ImViewer.MEDIUM:
            return ToolBar.MEDIUM;
        case ImViewer.LOW:
            return ToolBar.LOW;
        }
    }

    /**
     * Returns the UI index of the currently selected compression level.
     * 
     * @return See above.
     */
    int getUICompressionLevel() {
        return toolBar.getUICompressionLevel();
    }

    /**
     * Returns <code>true</code> if the image is compressed, 
     * <code>false</code> otherwise.
     * 
     * @return See above.
     */
    boolean isImageCompressed() {
        return model.isImageCompressed();
    }

    /**
     * Returns the color of the image's background.
     * 
     * @return See above.
     */
    Color getBackgroundColor() {
        ColorCheckBoxMenuItem b;
        Enumeration e;
        for (e = bgColorGroup.getElements(); e.hasMoreElements();) {
            b = (ColorCheckBoxMenuItem) e.nextElement();
            if (b.isSelected())
                return b.getColor();
        }
        return null;
    }

    /**
     * Returns the index of the scale bar.
     * 
     * @return See above.
     */
    int getScaleBarIndex() {
        if (scaleBarGroup == null)
            return -1;
        JCheckBoxMenuItem item;
        Enumeration e;
        for (e = scaleBarGroup.getElements(); e.hasMoreElements();) {
            item = (JCheckBoxMenuItem) e.nextElement();
            if (item.isSelected())
                return ((UnitBarSizeAction) item.getAction()).getIndex();
        }
        return -1;
    }

    /** Shows the list of users who viewed the image.  */
    void showUsersList() {
        if (usersMenu == null) {
            usersMenu = new UsersPopupMenu(model);
            usersMenu.addPropertyChangeListener(controller);
        }
        usersMenu.show(source, location.x, location.y);
        source = null;
        location = null;
    }

    /**
     * Sets the location and the source where to pop up the menu.
     * 
     * @param source   The source to set.
     * @param location   The location to set.
     */
    void setLocationAndSource(Component source, Point location) {
        this.source = source;
        this.location = location;
    }

    /** 
     * Sets the magnification for the grid view.
     * 
     * @param factor The value to set.
     */
    void setGridMagnificationFactor(double factor) {
        if (model.getTabbedIndex() == ImViewer.GRID_INDEX)
            setMagnificationStatus(factor, ZoomAction.getIndex(factor));
        JCheckBoxMenuItem b;
        Enumeration e;
        Action a;
        int zoomIndex = ZoomGridAction.getIndex(factor);
        for (e = zoomingGridGroup.getElements(); e.hasMoreElements();) {
            b = (JCheckBoxMenuItem) e.nextElement();
            a = b.getAction();
            if (a instanceof ZoomGridAction) {
                b.removeActionListener(a);
                b.setSelected(((ZoomGridAction) a).getIndex() == zoomIndex);
                b.setAction(a);
            }
        }
        controlPane.setGridMagnificationFactor((int) (factor * 10));
    }

    /**
     * Adds the component specified by the passed index to the display
     * 
     * @param index The index identifying the UI component.
     */
    void showView(int index) {
        switch (index) {
        case ImViewer.VIEW_INDEX:
            tabs.insertClosableComponent(viewPanel);
            break;
        case ImViewer.GRID_INDEX:
            tabs.insertClosableComponent(gridViewPanel);
            break;
        case ImViewer.PROJECTION_INDEX:
            tabs.insertClosableComponent(projectionViewPanel);
            break;
        }
        tabs.validate();
        tabs.repaint();
        restoreSize = tabs.getSize();
    }

    /**
     * Returns the id of the currently selected pixels.
     * 
     * @return See above.
     */
    long getPixelsID() {
        return model.getPixelsID();
    }

    /**
     * Returns <code>true</code> if there is some rendering settings to save,
     * <code>false</code> otherwise.
     * 
     * @return See above.
     */
    boolean hasSettingsToPaste() {
        return model.hasRndToPaste();
    }

    /**
     * Sets the {@link #pasteButton} enable.
     * 
     * @param b Pass <code>true</code> to enable the button, <code>false</code>
     *          otherwise.
     */
    void enablePasteButton(boolean b) {
        toolBar.enablePasteButton(b);
    }

    /** 
     * Shows the plane information.
     * 
     * @param show    Pass <code>true</code> to show the dialog, 
     *             <code>false</code> to hide it.
     * @param comp The component to show.
     */
    void showPlaneInfoDetails(PlaneInfoComponent comp) {
        if (comp == null)
            return;
        if (channelDialog != null) {
            JComponent c = channelDialog.getCanvas();
            channelDialog.closeWindow();
            hideAnimation();
            channelDialog = null;
            if (c == comp.getContent())
                return;
        }
        if (infoDialog != null) {
            infoDialog.closeWindow();
            hideAnimation();
            infoDialog = null;
        }
        channelDialog = new TinyDialog(this, comp.getContent(), TinyDialog.CLOSE_ONLY);
        channelDialog.addPropertyChangeListener(new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent evt) {
                if (TinyDialog.CLOSED_PROPERTY.equals(evt.getPropertyName())) {
                    channelDialog.closeWindow();
                    hideAnimation();
                    channelDialog = null;
                }
            }
        });
        channelDialog.pack();
        Point p;
        JPanel glass = (JPanel) getGlassPane();
        if (glass.getLayout() == null) {
            Dimension d = channelDialog.getPreferredSize();
            d = new Dimension(d.width + 20, d.height);
            channelDialog.setSize(d);
            channelDialog.setPreferredSize(d);
            p = new Point(0, statusBar.getPreferredSize().height);
        } else {
            p = new Point(0, 2 * statusBar.getPreferredSize().height);
        }
        //setCloseAfter(true);
        showJDialogAsSheet(channelDialog, p, UP_MIDDLE);
    }

    /** 
     * Displays information about the image.
     * 
     * @param comp
     */
    void showImageInfo(JComponent comp) {
        if (comp == null)
            return;
        if (infoDialog != null) {
            infoDialog.closeWindow();
            hideAnimation();
            infoDialog = null;
            return;
        }
        if (channelDialog != null) {
            channelDialog.closeWindow();
            hideAnimation();
            channelDialog = null;
        }
        infoDialog = new TinyDialog(this, comp, TinyDialog.CLOSE_ONLY);
        infoDialog.addPropertyChangeListener(new PropertyChangeListener() {

            public void propertyChange(PropertyChangeEvent evt) {
                if (TinyDialog.CLOSED_PROPERTY.equals(evt.getPropertyName())) {
                    infoDialog.closeWindow();
                    hideAnimation();
                    infoDialog = null;
                }
            }
        });
        infoDialog.pack();
        Point p;
        JPanel glass = (JPanel) getGlassPane();
        if (glass.getLayout() == null) {
            Dimension d = infoDialog.getPreferredSize();
            d = new Dimension(d.width, d.height + 20);
            infoDialog.setSize(d);
            infoDialog.setPreferredSize(d);
            p = new Point(0, statusBar.getPreferredSize().height);
        } else {
            p = new Point(0, 2 * statusBar.getPreferredSize().height);

        }
        //setCloseAfter(true);
        showJDialogAsSheet(infoDialog, p, UP_LEFT);
    }

    /**
     * Returns the maximum number of z-sections.
     * 
     * @return See above.
     */
    int getMaxZ() {
        return model.getMaxZ();
    }

    /**
     * Returns the index of the selected tab.
     * 
     * @return See above.
     */
    int getTabbedIndex() {
        return model.getTabbedIndex();
    }

    /**
     * Returns the lower bound of the z-section to project.
     * 
     * @return See above.
     */
    int getProjectionStartZ() {
        return controlPane.getProjectionStartZ();
    }

    /**
     * Returns the lower bound of the z-section to project.
     * 
     * @return See above.
     */
    int getProjectionEndZ() {
        return controlPane.getProjectionEndZ();
    }

    /**
     * Returns the stepping used for the projection.
     * 
     * @return See above.
     */
    int getProjectionStepping() {
        return controlPane.getProjectionStepping();
    }

    /**
     * Returns the type of projection.
     * 
     * @return See above.
     */
    int getProjectionType() {
        return controlPane.getProjectionType();
    }

    /**
     * Returns a textual version of the type of projection.
     * 
     * @return See above.
     */
    String getProjectionTypeName() {
        return controlPane.getProjectionTypeName();
    }

    /**
      * Sets to <code>true</code> if loading data, to <code>false</code>
      * otherwise.
      * 
      * @param busy    Pass <code>true</code> while loading data, 
      *             <code>false</code> otherwise.
      */
    void setStatus(boolean busy) {
        toolBar.setStatus(busy);
    }

    /** Clears the history. */
    void clearHistory() {
        if (historyUI != null)
            historyUI.clearHistory();
    }

    /**
     * Returns the last projection parameters.
     * 
     * @return See above.
     */
    ProjectionParam getLastProjRef() {
        return model.getLastProjRef();
    }

    /** 
     * Creates an history item. 
    * 
    * @param ref The projection ref or <code>null</code>.
    */
    void createHistoryItem(ProjectionParam ref) {
        /* 28/02 Back for Beta 4.1
        if (model.isPlayingChannelMovie()) return;
        String title = null;
        if (model.getHistory() == null) 
           title = "Initial "+ImViewer.TITLE_VIEW_INDEX;
        HistoryItem node = model.createHistoryItem();
        if (node == null) return;
        if (title != null) node.setTitle(title);
        node.setProjectionRef(ref);
        node.setDefaultT(model.getDefaultT());
        node.addPropertyChangeListener(controller);
        //add Listener to node.
        if (nodeListener == null) {
           nodeListener = new MouseAdapter() {
            
        public void mousePressed(MouseEvent evt) {
           HistoryItem item = findParentDisplay(evt.getSource());
           try {
              if (item.getIndex() == ImViewer.PROJECTION_INDEX) {
                 model.setLastProjectionRef(
                       item.getProjectionRef());
                 model.setLastProjectionTime(item.getDefaultT());
              }
                     
              setCursor(Cursor.getPredefinedCursor(
                    Cursor.WAIT_CURSOR));
              RndProxyDef def = item.getRndSettings();
              model.resetMappingSettings(def, true);
              setCursor(Cursor.getPredefinedCursor(
                    Cursor.DEFAULT_CURSOR));
              resetDefaults();
              showView(item.getIndex());
              controller.renderXYPlane();
              model.setLastRndDef(def);
           } catch (Exception e) {}
        }
           };
        }
        node.addMouseListenerToComponents(nodeListener);
        addHistoryItem(node);
        */
    }

    /**
     * Replaces the component in the display either icon or busy label
     * depending on the passed parameter.
     * 
     * @param b Pass <code>true</code> to indicate the creation,
     *          <code>false</code> to indicate that the creation is done.
     */
    void setMeasurementLaunchingStatus(boolean b) {
        Action a = controller.getAction(ImViewerControl.MEASUREMENT_TOOL);
        a.setEnabled(!b);
        toolBar.setMeasurementLaunchingStatus(b);
    }

    /**
     * Updates the scale bar menu.
     * 
     * @param index The selected index.
     */
    void setDefaultScaleBarMenu(int index) {
        defaultIndex = index;
    }

    /**
     * Shows or hides a busy label indicating the on-going creation of the
     * grid image.
     * 
     * @param busy  Pass <code>true</code> to indicate the on-going creation,
     *             <code>false</code> when it is finished.
     */
    void createGridImage(boolean busy) {
        controlPane.createGridImage(busy);
    }

    /**
      * Returns the parent of the image or <code>null</code> 
      * if no context specified.
      * 
      * @return See above.
      */
    DataObject getParentObject() {
        return model.getParent();
    }

    /**
     * Returns the image to view.
     * 
     * @return See above.
     */
    ImageData getImage() {
        return model.getImage();
    }

    /**
     * Returns the ID of the viewed image.
     * 
     * @return See above.
     */
    long getImageID() {
        return model.getImageID();
    }

    /**
     * Returns <code>true</code> if the passed object is one of the
     * channel buttons, <code>false</code> otherwise.
     * 
     * @param source The object to handle.
     * @return See above.
     */
    boolean isSourceDisplayed(Object source) {
        return controlPane.isSourceDisplayed(source);
    }

    /**
     * Returns <code>true</code> if the viewer should be opened in a 
     * separate window, <code>false</code> otherwise.
     * The default value is <code>true</code>.
     * 
     * @return See above.
     */
    boolean isSeparateWindow() {
        return model.isSeparateWindow();
    }

    /** Builds the UI to handle overlays. */
    void buildOverlays() {
        controlPane.buildOverlays();
    }

    /** 
     * Renders the image with the overlays or not. 
     * 
     * @param index    The index of the selected channel.
     * @param selected  Pass <code>true</code> to turn the overlay on,
     *                <code>false</code> to turn it off.
     */
    void renderOverlays(int index, boolean selected) {
        controlPane.renderOverlays(index, selected);
    }

    /**
     * Returns <code>true</code> if the overlays are turned on,
     * <code>false</code> otherwise.
     * 
     * @return See above.
     */
    boolean isOverlayActive() {
        return controlPane.isOverlayActive();
    }

    /**
     * Returns the selected overlays if displayed otherwise returns 
     * <code>null</code>.
     * 
     * @return See above.
     */
    Map<Long, Integer> getSelectedOverlays() {
        return controlPane.getSelectedOverlays();
    }

    /** Invokes when the color model changes. */
    void onColorModelChanged() {
        controlPane.onColorModelChanged();
    }

    /**
     * Returns <code>true</code> if it is a large image, 
     * <code>false</code> otherwise.
     * 
     * @return See above.
     */
    boolean isBigImage() {
        return model.isBigImage();
    }

    /**
     * Returns <code>true</code> if it is a large image,
     * <code>false</code> otherwise.
     * 
     * @return See above.
     */
    boolean isLargePlane() {
        return model.isLargePlane();
    }

    /**
     * Returns the view as a component.
     * 
     * @return See above.
     */
    JComponent asComponent() {
        if (component != null)
            return component;
        component = new JPanel();
        component.setLayout(new BorderLayout(0, 0));
        component.add(toolBar, BorderLayout.NORTH);
        component.add(mainComponent, BorderLayout.CENTER);
        return component;
    }

    /** 
     * Re-attaches the viewer. 
     * This method should only be invoked when the image has been embedded
     * and detached.
     */
    void rebuild() {
        if (component == null)
            return;
        //component.add(mainComponent, BorderLayout.CENTER);
        component.add(statusBar, BorderLayout.SOUTH);
        Container c = getContentPane();
        toolBar.setSeparateWindow();
        c.setLayout(new BorderLayout(0, 0));
        c.add(component, BorderLayout.CENTER);

    }

    /** Invokes when the rendering control is loaded. */
    void onRndLoaded() {
        clearZoomMenu(zoomingGroup, zoomMenu);
        clearZoomMenu(zoomingGridGroup, zoomGridMenu);
        ViewerPreferences pref = ImViewerFactory.getPreferences();
        createZoomMenu(pref, false);
        int index = ZoomAction.getIndex(model.getZoomFactor());
        if (model.isBigImage())
            index = model.getSelectedResolutionLevel();
        setMagnificationStatus(model.getZoomFactor(), index);
        controlPane.resetZoomValues();
    }

    /** Sets the image data.*/
    void setImageData() {
        setTitle(model.getImageTitle());
        statusBar.formatToolTip();
    }

    /**
      * Returns the group the image belongs to.
      * 
      * @return See above.
      */
    GroupData getSelectedGroup() {
        return model.getSelectedGroup();
    }

    /**
     * Updates the component displaying the channels' details after update.
     */
    void onChannelUpdated() {
        controlPane.onChannelUpdated();
    }

    /**
    * Sets the compression index.
    * 
    * @param index
    */
    void resetCompressionLevel(int index) {
        toolBar.setCompressionLevel(index);
    }

    /** 
     * Overridden to the set the location of the {@link ImViewer}.
     * @see TopWindow#setOnScreen() 
     */
    public void setOnScreen() {
        if (model != null) {
            Browser browser = model.getBrowser();
            Rectangle r = null;
            if (browser != null) {
                JComponent comp = browser.getUI();
                Dimension size = comp.getPreferredSize();
                int w = size.width;
                int h = size.height;
                ViewerPreferences pref = ImViewerFactory.getPreferences();
                if (pref != null) {
                    r = pref.getViewerBounds();
                    w = r.width;
                    h = r.height;
                    if (w <= 0)
                        w = size.width;
                    if (h <= 0)
                        h = size.height;
                }
                if (pref != null) {
                    setBounds(r.x, r.y, w, h);
                    setVisible(true);
                } else
                    packWindow();
            } else
                packWindow();
            UIUtilities.incrementRelativeToAndShow(model.getRequesterBounds(), this);
        } else {
            packWindow();
            UIUtilities.incrementRelativeToAndShow(null, this);
        }
    }

    /** 
     * Overridden to show or hide the glass pane.
     */
    public void hideAnimation() {
        JPanel glass = (JPanel) getGlassPane();
        hideAnimation(glass.getLayout() == null);
    }

}