/* * Copyright 2010-2015 Institut Pasteur. * * This file is part of Icy. * * Icy 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 3 of the License, or * (at your option) any later version. * * Icy 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 Icy. If not, see <http://www.gnu.org/licenses/>. */ package icy.gui.viewer; import icy.action.CanvasActions; import icy.action.CanvasActions.ToggleLayersAction; import icy.action.ViewerActions; import icy.action.WindowActions; import icy.canvas.IcyCanvas; import icy.canvas.IcyCanvas2D; import icy.canvas.IcyCanvasEvent; import icy.canvas.IcyCanvasListener; import icy.common.MenuCallback; import icy.common.listener.ProgressListener; import icy.gui.component.button.IcyButton; import icy.gui.component.button.IcyToggleButton; import icy.gui.component.renderer.LabelComboBoxRenderer; import icy.gui.dialog.ConfirmDialog; import icy.gui.dialog.MessageDialog; import icy.gui.dialog.SaverDialog; import icy.gui.frame.IcyFrame; import icy.gui.frame.IcyFrameAdapter; import icy.gui.frame.IcyFrameEvent; import icy.gui.lut.LUTViewer; import icy.gui.lut.abstract_.IcyLutViewer; import icy.gui.plugin.PluginComboBoxRenderer; import icy.gui.util.ComponentUtil; import icy.gui.viewer.ViewerEvent.ViewerEventType; import icy.image.IcyBufferedImage; import icy.image.lut.LUT; import icy.main.Icy; import icy.plugin.PluginLoader; import icy.plugin.PluginLoader.PluginLoaderEvent; import icy.plugin.PluginLoader.PluginLoaderListener; import icy.plugin.interface_.PluginCanvas; import icy.preferences.GeneralPreferences; import icy.sequence.DimensionId; import icy.sequence.Sequence; import icy.sequence.SequenceEvent; import icy.sequence.SequenceListener; import icy.system.IcyExceptionHandler; import icy.system.IcyHandledException; import icy.system.thread.ThreadUtil; import icy.util.GraphicsUtil; import icy.util.Random; import icy.util.StringUtil; import java.awt.AlphaComposite; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Dimension; import java.awt.Font; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.KeyboardFocusManager; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.geom.Rectangle2D; import java.awt.image.BufferedImage; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import javax.swing.ActionMap; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.InputMap; import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JToolBar; import javax.swing.WindowConstants; import javax.swing.event.EventListenerList; /** * Viewer send an event if the IcyCanvas change. * * @author Fabrice de Chaumont & Stephane */ public class Viewer extends IcyFrame implements KeyListener, SequenceListener, IcyCanvasListener, PluginLoaderListener { private class ViewerMainPanel extends JPanel { public ViewerMainPanel() { super(); } public void drawTextCenter(Graphics2D g, String text, float alpha) { final Rectangle2D rect = GraphicsUtil.getStringBounds(g, text); final int w = (int) rect.getWidth(); final int h = (int) rect.getHeight(); final int x = (getWidth() - (w + 8 + 2)) / 2; final int y = (getHeight() - (h + 8 + 2)) / 2; g.setColor(Color.gray); g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, alpha)); g.fillRoundRect(x, y, w + 8, h + 8, 8, 8); g.setColor(Color.white); g.drawString(text, x + 4, y + 2 + h); } @Override public void paint(Graphics g) { // currently modifying canvas ? super.paint(g); // display a message if (settingCanvas) { final Graphics2D g2 = (Graphics2D) g.create(); g2.setFont(new Font("Arial", Font.BOLD, 16)); g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); drawTextCenter(g2, "Loading canvas...", 0.8f); g2.dispose(); } } } /** * associated LUT */ private LUT lut; /** * associated canvas */ private IcyCanvas canvas; /** * associated sequence */ Sequence sequence; /***/ private final EventListenerList listeners = new EventListenerList(); /** * GUI */ private JToolBar toolBar; private ViewerMainPanel mainPanel; private LUTViewer lutViewer; JComboBox canvasComboBox; JComboBox lockComboBox; IcyToggleButton layersEnabledButton; IcyButton screenShotButton; IcyButton screenShotAlternateButton; IcyButton duplicateButton; IcyButton switchStateButton; /** * internals */ boolean initialized; private final Runnable lutUpdater; boolean settingCanvas; public Viewer(Sequence sequence, boolean visible) { super("Viewer", true, true, true, true); if (sequence == null) throw new IllegalArgumentException("Can't open a null sequence."); this.sequence = sequence; // default canvas = null; lut = null; lutViewer = null; initialized = false; settingCanvas = false; lutUpdater = new Runnable() { @Override public void run() { // don't need to update that too much ThreadUtil.sleep(1); ThreadUtil.invokeNow(new Runnable() { @Override public void run() { final LUT lut = getLut(); // closed --> ignore if (lut != null) { // refresh LUT viewer setLutViewer(new LUTViewer(Viewer.this, lut)); // notify fireViewerChanged(ViewerEventType.LUT_CHANGED); } } }); } }; mainPanel = new ViewerMainPanel(); mainPanel.setLayout(new BorderLayout()); // set menu directly in system menu so we don't need a extra MenuBar setSystemMenuCallback(new MenuCallback() { @Override public JMenu getMenu() { return Viewer.this.getMenu(); } }); // build tool bar buildToolBar(); // create a new compatible LUT final LUT lut = sequence.createCompatibleLUT(); // restore user LUT if needed if (sequence.hasUserLUT()) { final LUT userLut = sequence.getUserLUT(); // restore colormaps (without alpha) lut.setColorMaps(userLut, false); // then restore scalers lut.setScalers(userLut); } // set lut (this modify lutPanel) setLut(lut); // set default canvas to first available canvas plugin (Canvas2D should be first) setCanvas(IcyCanvas.getCanvasPluginNames().get(0)); setLayout(new BorderLayout()); add(toolBar, BorderLayout.NORTH); add(mainPanel, BorderLayout.CENTER); // setting frame refreshViewerTitle(); setFocusable(true); // set position depending window mode setLocationInternal(20 + Random.nextInt(100), 20 + Random.nextInt(60)); setLocationExternal(100 + Random.nextInt(200), 100 + Random.nextInt(150)); setSize(640, 480); // initial position in sequence if (sequence.isEmpty()) setPositionZ(0); else setPositionZ(((sequence.getSizeZ() + 1) / 2) - 1); addFrameListener(new IcyFrameAdapter() { @Override public void icyFrameOpened(IcyFrameEvent e) { if (!initialized) { if ((Viewer.this.sequence != null) && !Viewer.this.sequence.isEmpty()) { adjustViewerToImageSize(); initialized = true; } } } @Override public void icyFrameActivated(IcyFrameEvent e) { Icy.getMainInterface().setActiveViewer(Viewer.this); } @Override public void icyFrameExternalized(IcyFrameEvent e) { refreshToolBar(); } @Override public void icyFrameInternalized(IcyFrameEvent e) { refreshToolBar(); } @Override public void icyFrameClosing(IcyFrameEvent e) { onClosing(); } }); addKeyListener(this); sequence.addListener(this); PluginLoader.addListener(this); // do this when viewer is initialized Icy.getMainInterface().registerViewer(this); // automatically add it to the desktop pane addToDesktopPane(); if (visible) { setVisible(true); requestFocus(); } else setVisible(false); // can be done after setVisible buildActionMap(); } public Viewer(Sequence sequence) { this(sequence, true); } void buildActionMap() { // global input map buildActionMap(getInputMap(JComponent.WHEN_FOCUSED), getActionMap()); } protected void buildActionMap(InputMap imap, ActionMap amap) { imap.put(WindowActions.gridTileAction.getKeyStroke(), WindowActions.gridTileAction.getName()); imap.put(WindowActions.horizontalTileAction.getKeyStroke(), WindowActions.horizontalTileAction.getName()); imap.put(WindowActions.verticalTileAction.getKeyStroke(), WindowActions.verticalTileAction.getName()); imap.put(CanvasActions.globalDisableSyncAction.getKeyStroke(), CanvasActions.globalDisableSyncAction.getName()); imap.put(CanvasActions.globalSyncGroup1Action.getKeyStroke(), CanvasActions.globalSyncGroup1Action.getName()); imap.put(CanvasActions.globalSyncGroup2Action.getKeyStroke(), CanvasActions.globalSyncGroup2Action.getName()); imap.put(CanvasActions.globalSyncGroup3Action.getKeyStroke(), CanvasActions.globalSyncGroup3Action.getName()); imap.put(CanvasActions.globalSyncGroup4Action.getKeyStroke(), CanvasActions.globalSyncGroup4Action.getName()); amap.put(WindowActions.gridTileAction.getName(), WindowActions.gridTileAction); amap.put(WindowActions.horizontalTileAction.getName(), WindowActions.horizontalTileAction); amap.put(WindowActions.verticalTileAction.getName(), WindowActions.verticalTileAction); amap.put(CanvasActions.globalDisableSyncAction.getName(), CanvasActions.globalDisableSyncAction); amap.put(CanvasActions.globalSyncGroup1Action.getName(), CanvasActions.globalSyncGroup1Action); amap.put(CanvasActions.globalSyncGroup2Action.getName(), CanvasActions.globalSyncGroup2Action); amap.put(CanvasActions.globalSyncGroup3Action.getName(), CanvasActions.globalSyncGroup3Action); amap.put(CanvasActions.globalSyncGroup4Action.getName(), CanvasActions.globalSyncGroup4Action); } /** * Called when user want to close viewer */ protected void onClosing() { // this sequence was not saved if ((sequence != null) && (sequence.getFilename() == null)) { // save new sequence enabled ? if (GeneralPreferences.getSaveNewSequence()) { final int res = ConfirmDialog.confirmEx("Save sequence", "Do you want to save '" + sequence.getName() + "' before closing it ?", ConfirmDialog.YES_NO_CANCEL_OPTION); switch (res) { case JOptionPane.YES_OPTION: // save the image new SaverDialog(sequence); case JOptionPane.NO_OPTION: setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); break; case JOptionPane.CANCEL_OPTION: default: setDefaultCloseOperation(WindowConstants.DO_NOTHING_ON_CLOSE); break; } return; } } // just close it setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); } /** * Called when viewer is closed.<br> * Release as much references we can here because of the * <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4759312"> * JInternalFrame bug</a>. */ @Override public void onClosed() { // notify close fireViewerClosed(); // remove listeners sequence.removeListener(this); if (canvas != null) canvas.removeCanvasListener(this); PluginLoader.removeListener(this); icy.main.Icy.getMainInterface().unRegisterViewer(this); // AWT JDesktopPane keep reference on last closed JInternalFrame // it's good to free as much reference we can here if (canvas != null) canvas.shutDown(); if (lutViewer != null) lutViewer.dispose(); if (mainPanel != null) mainPanel.removeAll(); if (toolBar != null) toolBar.removeAll(); // remove all listeners for this viewer ViewerListener[] vls = listeners.getListeners(ViewerListener.class); for (ViewerListener vl : vls) listeners.remove(ViewerListener.class, vl); lutViewer = null; mainPanel = null; canvas = null; sequence = null; lut = null; toolBar = null; canvasComboBox = null; lockComboBox = null; duplicateButton = null; layersEnabledButton = null; screenShotAlternateButton = null; screenShotButton = null; switchStateButton = null; super.onClosed(); } void adjustViewerToImageSize() { if (canvas instanceof IcyCanvas2D) { final IcyCanvas2D cnv = (IcyCanvas2D) canvas; final int ix = cnv.getImageSizeX(); final int iy = cnv.getImageSizeY(); if ((ix > 0) && (iy > 0)) { // find scale factor to fit image in a 640x540 sized window // and limit zoom to 100% final double scale = Math.min(Math.min(640d / ix, 540d / iy), 1d); cnv.setScaleX(scale); cnv.setScaleY(scale); // this actually resize viewer as canvas size depend from it cnv.fitCanvasToImage(); } } // minimum size to start : 400, 240 final Dimension size = new Dimension(Math.max(getWidth(), 400), Math.max(getHeight(), 240)); // minimum size global : 200, 140 final Dimension minSize = new Dimension(200, 140); // adjust size of both frames setSizeExternal(size); setSizeInternal(size); setMinimumSizeInternal(minSize); setMinimumSizeExternal(minSize); } /** * Rebuild and return viewer menu */ JMenu getMenu() { final JMenu result = getDefaultSystemMenu(); final JMenuItem overlayItem = new JMenuItem(CanvasActions.toggleLayersAction); if ((canvas != null) && canvas.isLayersVisible()) overlayItem.setText("Hide layers"); else overlayItem.setText("Show layers"); final JMenuItem duplicateItem = new JMenuItem(ViewerActions.duplicateAction); // set menu result.insert(overlayItem, 0); result.insertSeparator(1); result.insert(duplicateItem, 2); return result; } protected void buildLockCombo() { final ArrayList<JLabel> labels = new ArrayList<JLabel>(); // get sync action labels labels.add(CanvasActions.disableSyncAction.getLabelComponent(true, false)); labels.add(CanvasActions.syncGroup1Action.getLabelComponent(true, false)); labels.add(CanvasActions.syncGroup2Action.getLabelComponent(true, false)); labels.add(CanvasActions.syncGroup3Action.getLabelComponent(true, false)); labels.add(CanvasActions.syncGroup4Action.getLabelComponent(true, false)); // build comboBox with lock id lockComboBox = new JComboBox(labels.toArray()); // set specific renderer lockComboBox.setRenderer(new LabelComboBoxRenderer(lockComboBox)); // limit size ComponentUtil.setFixedWidth(lockComboBox, 48); lockComboBox.setToolTipText("Select synchronisation group"); // don't want focusable here lockComboBox.setFocusable(false); // needed because of VTK lockComboBox.setLightWeightPopupEnabled(false); // action on canvas change lockComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // adjust lock id setViewSyncId(lockComboBox.getSelectedIndex()); } }); } void buildCanvasCombo() { // build comboBox with canvas plugins canvasComboBox = new JComboBox(IcyCanvas.getCanvasPluginNames().toArray()); // specific renderer canvasComboBox.setRenderer(new PluginComboBoxRenderer(canvasComboBox, false)); // limit size ComponentUtil.setFixedWidth(canvasComboBox, 48); canvasComboBox.setToolTipText("Select canvas type"); // don't want focusable here canvasComboBox.setFocusable(false); // needed because of VTK canvasComboBox.setLightWeightPopupEnabled(false); // action on canvas change canvasComboBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { // set selected canvas setCanvas((String) canvasComboBox.getSelectedItem()); } }); } /** * build the toolBar */ protected void buildToolBar() { // build combo box buildLockCombo(); buildCanvasCombo(); // build buttons layersEnabledButton = new IcyToggleButton(new ToggleLayersAction(true)); layersEnabledButton.setHideActionText(true); layersEnabledButton.setFocusable(false); layersEnabledButton.setSelected(true); screenShotButton = new IcyButton(CanvasActions.screenShotAction); screenShotButton.setFocusable(false); screenShotButton.setHideActionText(true); screenShotAlternateButton = new IcyButton(CanvasActions.screenShotAlternateAction); screenShotAlternateButton.setFocusable(false); screenShotAlternateButton.setHideActionText(true); duplicateButton = new IcyButton(ViewerActions.duplicateAction); duplicateButton.setFocusable(false); duplicateButton.setHideActionText(true); // duplicateButton.setToolTipText("Duplicate view (no data duplication)"); switchStateButton = new IcyButton(getSwitchStateAction()); switchStateButton.setFocusable(false); switchStateButton.setHideActionText(true); // and build the toolbar toolBar = new JToolBar(); toolBar.setFloatable(false); // so we don't have any border toolBar.setBorder(BorderFactory.createEmptyBorder(1, 0, 1, 0)); ComponentUtil.setPreferredHeight(toolBar, 26); updateToolbarComponents(); } void updateToolbarComponents() { if (toolBar != null) { toolBar.removeAll(); toolBar.add(lockComboBox); toolBar.addSeparator(); toolBar.add(canvasComboBox); toolBar.addSeparator(); toolBar.add(layersEnabledButton); if (canvas != null) canvas.customizeToolbar(toolBar); toolBar.add(Box.createHorizontalGlue()); toolBar.addSeparator(); toolBar.add(screenShotButton); toolBar.add(screenShotAlternateButton); toolBar.addSeparator(); toolBar.add(duplicateButton); toolBar.add(switchStateButton); } } void refreshLockCombo() { final int syncId = getViewSyncId(); lockComboBox.setEnabled(isSynchronizedViewSupported()); lockComboBox.setSelectedIndex(syncId); switch (syncId) { case 0: lockComboBox.setBackground(Color.gray); lockComboBox.setToolTipText("Synchronization disabled"); break; case 1: lockComboBox.setBackground(Color.green); lockComboBox.setToolTipText("Full synchronization group 1 (view and Z/T position)"); break; case 2: lockComboBox.setBackground(Color.yellow); lockComboBox.setToolTipText("Full synchronization group 2 (view and Z/T position)"); break; case 3: lockComboBox.setBackground(Color.blue); lockComboBox.setToolTipText("View synchronization group (view synched but not Z/T position)"); break; case 4: lockComboBox.setBackground(Color.red); lockComboBox.setToolTipText("Slice synchronization group (Z/T position synched but not view)"); break; } } void refreshCanvasCombo() { if (canvas != null) { // get plugin class name for this canvas final String pluginName = IcyCanvas.getPluginClassName(canvas.getClass().getName()); if (pluginName != null) { // align canvas combo to plugin name if (!canvasComboBox.getSelectedItem().equals(pluginName)) canvasComboBox.setSelectedItem(pluginName); } } } void refreshToolBar() { // FIXME : switchStateButton stay selected after action final boolean layersVisible = (canvas != null) ? canvas.isLayersVisible() : false; layersEnabledButton.setSelected(layersVisible); if (layersVisible) layersEnabledButton.setToolTipText("Hide layers"); else layersEnabledButton.setToolTipText("Show layers"); // refresh combos refreshLockCombo(); refreshCanvasCombo(); } /** * @return the sequence */ public Sequence getSequence() { return sequence; } /** * Set the specified LUT for the viewer. */ public void setLut(LUT value) { if ((lut != value) && sequence.isLutCompatible(value)) { // set new lut & notify change lut = value; lutChanged(); } } /** * Returns the viewer LUT */ public LUT getLut() { // have to test this as we release sequence reference on closed if (sequence == null) return lut; // sequence can be asynchronously modified so we have to test change on Getter if ((lut == null) || !sequence.isLutCompatible(lut)) { // sequence type has changed, we need to recreate a compatible LUT final LUT newLut = sequence.createCompatibleLUT(); // keep the color map of previous LUT if they have the same number of channels if ((lut != null) && (lut.getNumChannel() == newLut.getNumChannel())) newLut.getColorSpace().setColorMaps(lut.getColorSpace(), true); // set the new lut setLut(newLut); } return lut; } /** * Set the specified canvas for the viewer (from the {@link PluginCanvas} class name). * * @see IcyCanvas#getCanvasPluginNames() */ public void setCanvas(String pluginClassName) { // not the same canvas ? if ((canvas == null) || !canvas.getClass().getName().equals(IcyCanvas.getCanvasClassName(pluginClassName))) { try { IcyCanvas newCanvas; settingCanvas = true; // show loading message mainPanel.paintImmediately(mainPanel.getBounds()); try { // try to create the new canvas newCanvas = IcyCanvas.create(pluginClassName, this); } catch (Throwable e) { if (e instanceof IcyHandledException) { // just ignore } else if (e instanceof UnsupportedOperationException) { MessageDialog.showDialog(e.getLocalizedMessage(), MessageDialog.ERROR_MESSAGE); } else if (e instanceof Exception) { IcyExceptionHandler.handleException(new ClassNotFoundException("Cannot find '" + pluginClassName + "' class --> cannot create the canvas.", e), true); } else IcyExceptionHandler.handleException(e, true); // create a new instance of current canvas newCanvas = IcyCanvas.create(canvas.getClass().getName(), this); } finally { settingCanvas = false; } final int saveX; final int saveY; final int saveZ; final int saveT; final int saveC; // save properties and shutdown previous canvas if (canvas != null) { // save position saveX = canvas.getPositionX(); saveY = canvas.getPositionY(); saveZ = canvas.getPositionZ(); saveT = canvas.getPositionT(); saveC = canvas.getPositionC(); canvas.removePropertyChangeListener(IcyCanvas.PROPERTY_LAYERS_VISIBLE, this); canvas.removeCanvasListener(this); // --> this actually can do some restore operation (as the palette) after creation of the new canvas canvas.shutDown(); // remove from mainPanel mainPanel.remove(canvas); } else saveX = saveY = saveZ = saveT = saveC = -1; // prepare new canvas newCanvas.addCanvasListener(this); newCanvas.addPropertyChangeListener(IcyCanvas.PROPERTY_LAYERS_VISIBLE, this); // add to mainPanel mainPanel.add(newCanvas, BorderLayout.CENTER); // restore position if (saveX != -1) newCanvas.setPositionX(saveX); if (saveY != -1) newCanvas.setPositionY(saveY); if (saveZ != -1) newCanvas.setPositionZ(saveZ); if (saveT != -1) newCanvas.setPositionT(saveT); if (saveC != -1) newCanvas.setPositionC(saveC); // canvas set :) canvas = newCanvas; } catch (Throwable e) { IcyExceptionHandler.handleException(e, true); } mainPanel.revalidate(); // refresh viewer menu (so overlay checkbox is correctly set) updateSystemMenu(); updateToolbarComponents(); refreshToolBar(); // fix the OSX lost keyboard focus on canvas change in detached mode. KeyboardFocusManager.getCurrentKeyboardFocusManager().upFocusCycle(getCanvas()); // notify canvas changed to listener fireViewerChanged(ViewerEventType.CANVAS_CHANGED); } } /** * @deprecated Use {@link #setCanvas(String)} instead. */ @Deprecated public void setCanvas(IcyCanvas value) { if (canvas == value) return; final int saveX; final int saveY; final int saveZ; final int saveT; final int saveC; if (canvas != null) { // save position saveX = canvas.getPositionX(); saveY = canvas.getPositionY(); saveZ = canvas.getPositionZ(); saveT = canvas.getPositionT(); saveC = canvas.getPositionC(); canvas.removePropertyChangeListener(IcyCanvas.PROPERTY_LAYERS_VISIBLE, this); canvas.removeCanvasListener(this); canvas.shutDown(); // remove from mainPanel mainPanel.remove(canvas); } else saveX = saveY = saveZ = saveT = saveC = -1; // set new canvas canvas = value; if (canvas != null) { canvas.addCanvasListener(this); canvas.addPropertyChangeListener(IcyCanvas.PROPERTY_LAYERS_VISIBLE, this); // add to mainPanel mainPanel.add(canvas, BorderLayout.CENTER); // restore position if (saveX != -1) canvas.setPositionX(saveX); if (saveY != -1) canvas.setPositionY(saveY); if (saveZ != -1) canvas.setPositionZ(saveZ); if (saveT != -1) canvas.setPositionT(saveT); if (saveC != -1) canvas.setPositionC(saveC); } mainPanel.revalidate(); // refresh viewer menu (so overlay checkbox is correctly set) updateSystemMenu(); updateToolbarComponents(); refreshToolBar(); // fix the OSX lost keyboard focus on canvas change in detached mode. KeyboardFocusManager.getCurrentKeyboardFocusManager().upFocusCycle(getCanvas()); // notify canvas changed to listener fireViewerChanged(ViewerEventType.CANVAS_CHANGED); } /** * Returns true if the viewer initialization (correct image resizing) is completed. */ public boolean isInitialized() { return initialized; } /** * Return the viewer Canvas object */ public IcyCanvas getCanvas() { return canvas; } /** * Return the viewer Canvas panel */ public JPanel getCanvasPanel() { if (canvas != null) return canvas.getPanel(); return null; } /** * Return the viewer Lut panel */ public LUTViewer getLutViewer() { return lutViewer; } /** * Set the {@link LUTViewer} for this viewer. */ public void setLutViewer(LUTViewer value) { if (lutViewer != value) { if (lutViewer != null) lutViewer.dispose(); lutViewer = value; } } /** * @deprecated Use {@link #getLutViewer()} instead */ @Deprecated public IcyLutViewer getLutPanel() { return getLutViewer(); } /** * @deprecated Use {@link #setLutViewer(LUTViewer)} instead. */ @Deprecated public void setLutPanel(IcyLutViewer lutViewer) { setLutViewer((LUTViewer) lutViewer); } /** * Return the viewer ToolBar object */ public JToolBar getToolBar() { return toolBar; } /** * @return current T (-1 if all selected/displayed) */ public int getPositionT() { if (canvas != null) return canvas.getPositionT(); return 0; } /** * Set the current T position (for multi frame sequence). */ public void setPositionT(int t) { if (canvas != null) canvas.setPositionT(t); } /** * @return current Z (-1 if all selected/displayed) */ public int getPositionZ() { if (canvas != null) return canvas.getPositionZ(); return 0; } /** * Set the current Z position (for stack sequence). */ public void setPositionZ(int z) { if (canvas != null) canvas.setPositionZ(z); } /** * @return current C (-1 if all selected/displayed) */ public int getPositionC() { if (canvas != null) return canvas.getPositionC(); return 0; } /** * Set the current C (channel) position (multi channel sequence) */ public void setPositionC(int c) { if (canvas != null) canvas.setPositionC(c); } /** * @deprecated Use {@link #getPositionT()} instead. */ @Deprecated public int getT() { return getPositionT(); } /** * @deprecated Use {@link #setPositionT(int)} instead. */ @Deprecated public void setT(int t) { setPositionT(t); } /** * @deprecated Use {@link #getPositionZ()} instead. */ @Deprecated public int getZ() { return getPositionZ(); } /** * @deprecated Use {@link #setPositionZ(int)} instead. */ @Deprecated public void setZ(int z) { setPositionZ(z); } /** * @deprecated Use {@link #getPositionZ()} instead. */ @Deprecated public int getC() { return getPositionC(); } /** * @deprecated Use {@link #setPositionZ(int)} instead. */ @Deprecated public void setC(int c) { setPositionC(c); } /** * Get maximum T value */ public int getMaxT() { if (canvas != null) return canvas.getMaxPositionT(); return 0; } /** * Get maximum Z value */ public int getMaxZ() { if (canvas != null) return canvas.getMaxPositionZ(); return 0; } /** * Get maximum C value */ public int getMaxC() { if (canvas != null) return canvas.getMaxPositionC(); return 0; } /** * return true if current canvas's viewer does support synchronized view */ public boolean isSynchronizedViewSupported() { if (canvas != null) return canvas.isSynchronizationSupported(); return false; } /** * @return the viewSyncId */ public int getViewSyncId() { if (canvas != null) return canvas.getSyncId(); return -1; } /** * Set the view synchronization group id (0 means unsynchronized). * * @param id * the view synchronization id to set * @see IcyCanvas#setSyncId(int) */ public boolean setViewSyncId(int id) { if (canvas != null) return canvas.setSyncId(id); return false; } /** * Return true if this viewer has its view synchronized */ public boolean isViewSynchronized() { if (canvas != null) return canvas.isSynchronized(); return false; } /** * Delegation for {@link IcyCanvas#getImage(int, int, int)} */ public IcyBufferedImage getImage(int t, int z, int c) { if (canvas != null) return canvas.getImage(t, z, c); return null; } /** * @deprecated Use {@link #getImage(int, int, int)} with C = -1 instead. */ @Deprecated public IcyBufferedImage getImage(int t, int z) { return getImage(t, z, -1); } /** * Get the current image * * @return current image */ public IcyBufferedImage getCurrentImage() { if (canvas != null) return canvas.getCurrentImage(); return null; } /** * Return the number of "selected" samples */ public int getNumSelectedSamples() { if (canvas != null) return canvas.getNumSelectedSamples(); return 0; } /** * @see icy.canvas.IcyCanvas#getRenderedImage(int, int, int, boolean) */ public BufferedImage getRenderedImage(int t, int z, int c, boolean canvasView) { if (canvas == null) return null; return canvas.getRenderedImage(t, z, c, canvasView); } /** * @see icy.canvas.IcyCanvas#getRenderedSequence(boolean, icy.common.listener.ProgressListener) */ public Sequence getRenderedSequence(boolean canvasView, ProgressListener progressListener) { if (canvas == null) return null; return canvas.getRenderedSequence(canvasView, progressListener); } /** * Returns the T navigation panel. */ protected TNavigationPanel getTNavigationPanel() { if (canvas == null) return null; return canvas.getTNavigationPanel(); } /** * Returns the frame rate (given in frame per second) for play command. */ public int getFrameRate() { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) return tNav.getFrameRate(); return 0; } /** * Sets the frame rate (given in frame per second) for play command. */ public void setFrameRate(int fps) { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) tNav.setFrameRate(fps); } /** * Returns true if <code>repeat</code> is enabled for play command. */ public boolean isRepeat() { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) return tNav.isRepeat(); return false; } /** * Set <code>repeat</code> mode for play command. */ public void setRepeat(boolean value) { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) tNav.setRepeat(value); } /** * Returns true if currently playing. */ public boolean isPlaying() { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) return tNav.isPlaying(); return false; } /** * Start sequence play. * * @see #stopPlay() * @see #setRepeat(boolean) */ public void startPlay() { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) tNav.startPlay(); } /** * Stop sequence play. * * @see #startPlay() */ public void stopPlay() { final TNavigationPanel tNav = getTNavigationPanel(); if (tNav != null) tNav.stopPlay(); } /** * Return true if only this viewer is currently displaying its attached sequence */ public boolean isUnique() { return Icy.getMainInterface().isUniqueViewer(this); } protected void lutChanged() { // can be called from external thread, replace it in AWT dispatch thread ThreadUtil.bgRunSingle(lutUpdater); } protected void positionChanged(DimensionId dim) { fireViewerChanged(ViewerEventType.POSITION_CHANGED, dim); } /** * Add a listener * * @param listener */ public void addListener(ViewerListener listener) { listeners.add(ViewerListener.class, listener); } /** * Remove a listener * * @param listener */ public void removeListener(ViewerListener listener) { listeners.remove(ViewerListener.class, listener); } void fireViewerChanged(ViewerEventType eventType, DimensionId dim) { final ViewerEvent event = new ViewerEvent(this, eventType, dim); for (ViewerListener viewerListener : listeners.getListeners(ViewerListener.class)) viewerListener.viewerChanged(event); } void fireViewerChanged(ViewerEventType event) { fireViewerChanged(event, DimensionId.NULL); } protected void fireViewerClosed() { for (ViewerListener viewerListener : listeners.getListeners(ViewerListener.class)) viewerListener.viewerClosed(this); } @Override public void keyPressed(KeyEvent e) { // forward to canvas if ((canvas != null) && (!e.isConsumed())) canvas.keyPressed(e); } @Override public void keyReleased(KeyEvent e) { // forward to canvas if ((canvas != null) && (!e.isConsumed())) canvas.keyReleased(e); } @Override public void keyTyped(KeyEvent e) { // forward to canvas if ((canvas != null) && (!e.isConsumed())) canvas.keyTyped(e); } /** * Change the frame's title. */ protected void refreshViewerTitle() { // have to test this as we release sequence reference on closed if (sequence != null) setTitle(sequence.getName()); } @Override public void sequenceChanged(SequenceEvent event) { switch (event.getSourceType()) { case SEQUENCE_META: final String meta = (String) event.getSource(); if (StringUtil.isEmpty(meta) || StringUtil.equals(meta, Sequence.ID_NAME)) refreshViewerTitle(); break; case SEQUENCE_DATA: break; case SEQUENCE_TYPE: // might need initialization if (!initialized && (sequence != null) && !sequence.isEmpty()) { adjustViewerToImageSize(); initialized = true; } // we update LUT on type change directly on getLut() method // // try to keep current LUT if possible if (!sequence.isLutCompatible(lut)) // need to update the lut according to the colormodel change setLut(sequence.createCompatibleLUT()); break; case SEQUENCE_COLORMAP: break; case SEQUENCE_COMPONENTBOUNDS: // refresh lut scalers from sequence default lut final LUT sequenceLut = sequence.getDefaultLUT(); if (!sequenceLut.isCompatible(lut) || (lutViewer == null)) lut.setScalers(sequenceLut); break; } } @Override public void sequenceClosed(Sequence sequence) { } @Override public void canvasChanged(IcyCanvasEvent event) { switch (event.getType()) { case POSITION_CHANGED: // common process on position change positionChanged(event.getDim()); break; case SYNC_CHANGED: ThreadUtil.invokeLater(new Runnable() { @Override public void run() { refreshLockCombo(); } }); break; } } @Override public void propertyChange(PropertyChangeEvent evt) { super.propertyChange(evt); // Canvas property "layer visible" changed ? if (StringUtil.equals(evt.getPropertyName(), IcyCanvas.PROPERTY_LAYERS_VISIBLE)) { refreshToolBar(); updateSystemMenu(); } } @Override public void pluginLoaderChanged(PluginLoaderEvent e) { ThreadUtil.invokeLater(new Runnable() { @Override public void run() { // refresh available canvas buildCanvasCombo(); // and refresh components refreshCanvasCombo(); updateToolbarComponents(); } }); } }