/*******************************************************************************
* Copyright (c) 2016 Weasis Team and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Nicolas Roduit - initial API and implementation
*******************************************************************************/
package org.weasis.base.viewer2d;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import org.osgi.framework.BundleContext;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.prefs.Preferences;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.weasis.base.viewer2d.dockable.DisplayTool;
import org.weasis.base.viewer2d.dockable.ImageTool;
import org.weasis.core.api.explorer.ObservableEvent;
import org.weasis.core.api.gui.Insertable.Type;
import org.weasis.core.api.gui.InsertableUtil;
import org.weasis.core.api.gui.util.ActionState;
import org.weasis.core.api.gui.util.ActionW;
import org.weasis.core.api.gui.util.Filter;
import org.weasis.core.api.gui.util.JMVUtils;
import org.weasis.core.api.gui.util.SliderChangeListener;
import org.weasis.core.api.gui.util.SliderCineListener;
import org.weasis.core.api.image.GridBagLayoutModel;
import org.weasis.core.api.media.data.ImageElement;
import org.weasis.core.api.media.data.MediaSeries;
import org.weasis.core.api.media.data.MediaSeriesGroup;
import org.weasis.core.api.media.data.Series;
import org.weasis.core.api.media.data.SeriesEvent;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.service.BundlePreferences;
import org.weasis.core.api.service.BundleTools;
import org.weasis.core.ui.docking.DockableTool;
import org.weasis.core.ui.docking.PluginTool;
import org.weasis.core.ui.editor.SeriesViewerListener;
import org.weasis.core.ui.editor.image.DefaultView2d;
import org.weasis.core.ui.editor.image.ImageViewerPlugin;
import org.weasis.core.ui.editor.image.MeasureToolBar;
import org.weasis.core.ui.editor.image.RotationToolBar;
import org.weasis.core.ui.editor.image.SynchView;
import org.weasis.core.ui.editor.image.ViewCanvas;
import org.weasis.core.ui.editor.image.ViewerToolBar;
import org.weasis.core.ui.editor.image.ZoomToolBar;
import org.weasis.core.ui.editor.image.dockable.MeasureTool;
import org.weasis.core.ui.editor.image.dockable.MiniTool;
import org.weasis.core.ui.util.ColorLayerUI;
import org.weasis.core.ui.util.DefaultAction;
import org.weasis.core.ui.util.PrintDialog;
import org.weasis.core.ui.util.Toolbar;
public class View2dContainer extends ImageViewerPlugin<ImageElement> implements PropertyChangeListener {
private static final Logger LOGGER = LoggerFactory.getLogger(View2dContainer.class);
public static final List<SynchView> SYNCH_LIST = Collections.synchronizedList(new ArrayList<SynchView>());
static {
SYNCH_LIST.add(SynchView.NONE);
SYNCH_LIST.add(SynchView.DEFAULT_STACK);
SYNCH_LIST.add(SynchView.DEFAULT_TILE);
}
public static final List<GridBagLayoutModel> LAYOUT_LIST =
Collections.synchronizedList(new ArrayList<GridBagLayoutModel>());
static {
LAYOUT_LIST.add(VIEWS_1x1);
LAYOUT_LIST.add(VIEWS_1x2);
LAYOUT_LIST.add(VIEWS_2x1);
LAYOUT_LIST.add(VIEWS_2x2_f2);
LAYOUT_LIST.add(VIEWS_2_f1x2);
LAYOUT_LIST.add(VIEWS_2x2);
LAYOUT_LIST.add(VIEWS_3x2);
LAYOUT_LIST.add(VIEWS_3x3);
LAYOUT_LIST.add(VIEWS_4x3);
LAYOUT_LIST.add(VIEWS_4x4);
}
// Static tools shared by all the View2dContainer instances, tools are registered when a container is selected
// Do not initialize tools in a static block (order initialization issue with eventManager), use instead a lazy
// initialization with a method.
public static final List<Toolbar> TOOLBARS = Collections.synchronizedList(new ArrayList<Toolbar>());
public static final List<DockableTool> TOOLS = Collections.synchronizedList(new ArrayList<DockableTool>());
private static volatile boolean initComponents = false;
public View2dContainer() {
this(VIEWS_1x1, null);
}
public View2dContainer(GridBagLayoutModel layoutModel, String uid) {
super(EventManager.getInstance(), layoutModel, uid, ViewerFactory.NAME, ViewerFactory.ICON, null);
setSynchView(SynchView.DEFAULT_STACK);
if (!initComponents) {
initComponents = true;
// Add standard toolbars
final BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
EventManager evtMg = EventManager.getInstance();
String bundleName = context.getBundle().getSymbolicName();
String componentName = InsertableUtil.getCName(this.getClass());
String key = "enable"; //$NON-NLS-1$
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(ViewerToolBar.class), key, true)) {
TOOLBARS.add(new ViewerToolBar<>(evtMg, evtMg.getMouseActions().getActiveButtons(),
BundleTools.SYSTEM_PREFERENCES, 10));
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(MeasureToolBar.class), key, true)) {
TOOLBARS.add(new MeasureToolBar(evtMg, 11));
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(ZoomToolBar.class), key, true)) {
TOOLBARS.add(new ZoomToolBar(evtMg, 20, true));
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(RotationToolBar.class), key, true)) {
TOOLBARS.add(new RotationToolBar(evtMg, 30));
}
PluginTool tool = null;
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(MiniTool.class), key, true)) {
tool = new MiniTool(MiniTool.BUTTON_NAME) {
@Override
public SliderChangeListener[] getActions() {
ArrayList<SliderChangeListener> listeners = new ArrayList<>(3);
ActionState seqAction = eventManager.getAction(ActionW.SCROLL_SERIES);
if (seqAction instanceof SliderChangeListener) {
listeners.add((SliderChangeListener) seqAction);
}
ActionState zoomAction = eventManager.getAction(ActionW.ZOOM);
if (zoomAction instanceof SliderChangeListener) {
listeners.add((SliderChangeListener) zoomAction);
}
ActionState rotateAction = eventManager.getAction(ActionW.ROTATION);
if (rotateAction instanceof SliderChangeListener) {
listeners.add((SliderChangeListener) rotateAction);
}
return listeners.toArray(new SliderChangeListener[listeners.size()]);
}
};
TOOLS.add(tool);
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(ImageTool.class), key, true)) {
tool = new ImageTool(ImageTool.BUTTON_NAME);
TOOLS.add(tool);
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(DisplayTool.class), key, true)) {
tool = new DisplayTool(DisplayTool.BUTTON_NAME);
TOOLS.add(tool);
eventManager.addSeriesViewerListener((SeriesViewerListener) tool);
}
if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName,
InsertableUtil.getCName(MeasureTool.class), key, true)) {
tool = new MeasureTool(eventManager);
TOOLS.add(tool);
}
InsertableUtil.sortInsertable(TOOLS);
Preferences prefs = BundlePreferences.getDefaultPreferences(context);
if (prefs != null) {
InsertableUtil.applyPreferences(TOOLBARS, prefs, bundleName, componentName, Type.TOOLBAR);
InsertableUtil.applyPreferences(TOOLS, prefs, bundleName, componentName, Type.TOOL);
}
}
}
@Override
public JMenu fillSelectedPluginMenu(JMenu menuRoot) {
if (menuRoot != null) {
menuRoot.removeAll();
if (eventManager instanceof EventManager) {
EventManager manager = (EventManager) eventManager;
JMVUtils.addItemToMenu(menuRoot, manager.getLutMenu(null));
JMVUtils.addItemToMenu(menuRoot, manager.getLutInverseMenu(null));
JMVUtils.addItemToMenu(menuRoot, manager.getFilterMenu(null));
menuRoot.add(new JSeparator());
JMVUtils.addItemToMenu(menuRoot, manager.getZoomMenu(null));
JMVUtils.addItemToMenu(menuRoot, manager.getOrientationMenu(null));
// JMVUtils.addItemToMenu(menuRoot, manager.getSortStackMenu(null));
menuRoot.add(new JSeparator());
menuRoot.add(manager.getResetMenu(null));
}
}
return menuRoot;
}
@Override
public List<DockableTool> getToolPanel() {
return TOOLS;
}
@Override
public void setSelected(boolean selected) {
if (selected) {
eventManager.setSelectedView2dContainer(this);
} else {
eventManager.setSelectedView2dContainer(null);
}
}
@Override
public void close() {
ViewerFactory.closeSeriesViewer(this);
super.close();
}
private boolean closeIfNoContent() {
if (getOpenSeries().isEmpty()) {
close();
handleFocusAfterClosing();
return true;
}
return false;
}
@Override
public void propertyChange(PropertyChangeEvent evt) {
if (evt instanceof ObservableEvent) {
ObservableEvent event = (ObservableEvent) evt;
ObservableEvent.BasicAction action = event.getActionCommand();
Object newVal = event.getNewValue();
if (newVal instanceof SeriesEvent) {
SeriesEvent event2 = (SeriesEvent) newVal;
if (ObservableEvent.BasicAction.ADD.equals(action)) {
SeriesEvent.Action action2 = event2.getActionCommand();
Object source = event2.getSource();
Object param = event2.getParam();
if (SeriesEvent.Action.ADD_IMAGE.equals(action2)) {
if (source instanceof Series) {
Series series = (Series) source;
ViewCanvas view2DPane = eventManager.getSelectedViewPane();
ImageElement img = view2DPane.getImage();
if (img != null && view2DPane.getSeries() == series) {
ActionState seqAction = eventManager.getAction(ActionW.SCROLL_SERIES);
if (seqAction instanceof SliderCineListener) {
SliderCineListener sliceAction = (SliderCineListener) seqAction;
if (param instanceof ImageElement) {
Filter<ImageElement> filter = (Filter<ImageElement>) view2DPane
.getActionValue(ActionW.FILTERED_SERIES.cmd());
int imgIndex =
series.getImageIndex(img, filter, view2DPane.getCurrentSortComparator());
if (imgIndex < 0) {
imgIndex = 0;
// add again the series for registering listeners
// (require at least one image)
view2DPane.setSeries(series, null);
}
if (imgIndex >= 0) {
sliceAction.setSliderMinMaxValue(1, series.size(filter), imgIndex + 1);
}
}
}
}
}
} else if (SeriesEvent.Action.PRELOADING.equals(action2)) {
if (source instanceof Series) {
Series s = (Series) source;
for (ViewCanvas<ImageElement> v : view2ds) {
if (s == v.getSeries()) {
v.getJComponent().repaint(v.getInfoLayer().getPreloadingProgressBound());
}
}
}
}
}
} else if (ObservableEvent.BasicAction.REMOVE.equals(action)) {
if (newVal instanceof MediaSeriesGroup) {
MediaSeriesGroup group = (MediaSeriesGroup) newVal;
// Patient Group
if (TagW.Group.equals(group.getTagID())) {
if (group.equals(getGroupID())) {
// Close the content of the plug-in
close();
handleFocusAfterClosing();
}
}
// Series Group
else if (TagW.SubseriesInstanceUID.equals(group.getTagID())) {
for (ViewCanvas<ImageElement> v : view2ds) {
if (newVal.equals(v.getSeries())) {
v.setSeries(null);
if (closeIfNoContent()) {
return;
}
}
}
}
}
} else if (ObservableEvent.BasicAction.REPLACE.equals(action)) {
if (newVal instanceof Series) {
Series series = (Series) newVal;
for (ViewCanvas<ImageElement> v : view2ds) {
MediaSeries<ImageElement> s = v.getSeries();
if (series.equals(s)) {
// Set to null to be sure that all parameters from the view are apply again to the Series
// (in case for instance it is the same series with more images)
v.setSeries(null);
v.setSeries(series, null);
}
}
}
}
}
}
@Override
public DefaultView2d<ImageElement> createDefaultView(String classType) {
return new View2d(eventManager);
}
@Override
public JComponent createUIcomponent(String clazz) {
if (DefaultView2d.class.getName().equals(clazz) || View2d.class.getName().equals(clazz)) {
return createDefaultView(clazz);
}
try {
// FIXME use classloader.loadClass or injection
Class<?> cl = Class.forName(clazz);
JComponent component = (JComponent) cl.newInstance();
if (component instanceof SeriesViewerListener) {
eventManager.addSeriesViewerListener((SeriesViewerListener) component);
}
return component;
} catch (Exception e) {
LOGGER.error("Cannot create {}", clazz, e); //$NON-NLS-1$
}
return null;
}
@Override
public synchronized List<Toolbar> getToolBar() {
return TOOLBARS;
}
@Override
public List<Action> getExportActions() {
return selectedImagePane == null ? super.getExportActions() : selectedImagePane.getExportToClipboardAction();
}
@Override
public int getViewTypeNumber(GridBagLayoutModel layout, Class<?> defaultClass) {
return ViewerFactory.getViewTypeNumber(layout, defaultClass);
}
@Override
public boolean isViewType(Class<?> defaultClass, String type) {
if (defaultClass != null) {
try {
Class<?> clazz = Class.forName(type);
return defaultClass.isAssignableFrom(clazz);
} catch (Exception e) {
LOGGER.error("Checking view type", e); //$NON-NLS-1$
}
}
return false;
}
@Override
public List<Action> getPrintActions() {
ArrayList<Action> actions = new ArrayList<>(1);
final String title = Messages.getString("View2dContainer.print_layout"); //$NON-NLS-1$
Consumer<ActionEvent> event = e -> {
ColorLayerUI layer = ColorLayerUI.createTransparentLayerUI(View2dContainer.this);
PrintDialog<?> dialog =
new PrintDialog<>(SwingUtilities.getWindowAncestor(View2dContainer.this), title, eventManager);
ColorLayerUI.showCenterScreen(dialog, layer);
};
DefaultAction printStd = new DefaultAction(title,
new ImageIcon(ImageViewerPlugin.class.getResource("/icon/16x16/printer.png")), event); //$NON-NLS-1$
printStd.putValue(Action.ACCELERATOR_KEY, KeyStroke.getKeyStroke(KeyEvent.VK_P, 0));
actions.add(printStd);
return actions;
}
@Override
public List<SynchView> getSynchList() {
return SYNCH_LIST;
}
@Override
public List<GridBagLayoutModel> getLayoutList() {
return LAYOUT_LIST;
}
}