/*******************************************************************************
* 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.dicom.viewer2d.dockable;
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.FlowLayout;
import java.beans.PropertyChangeEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Enumeration;
import java.util.List;
import java.util.Optional;
import javax.swing.ImageIcon;
import javax.swing.JCheckBox;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTree;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreeNode;
import javax.swing.tree.TreePath;
import org.weasis.core.api.gui.util.ActionW;
import org.weasis.core.api.gui.util.JMVUtils;
import org.weasis.core.api.gui.util.SliderChangeListener;
import org.weasis.core.api.image.OpManager;
import org.weasis.core.api.image.WindowOp;
import org.weasis.core.api.media.data.ImageElement;
import org.weasis.core.api.media.data.Series;
import org.weasis.core.ui.docking.PluginTool;
import org.weasis.core.ui.editor.SeriesViewerEvent;
import org.weasis.core.ui.editor.SeriesViewerEvent.EVENT;
import org.weasis.core.ui.editor.SeriesViewerListener;
import org.weasis.core.ui.editor.image.ImageViewerPlugin;
import org.weasis.core.ui.editor.image.Panner;
import org.weasis.core.ui.editor.image.ViewCanvas;
import org.weasis.core.ui.model.layer.LayerAnnotation;
import org.weasis.core.ui.model.layer.LayerType;
import org.weasis.dicom.codec.DicomImageElement;
import org.weasis.dicom.codec.display.OverlayOp;
import org.weasis.dicom.codec.display.ShutterOp;
import org.weasis.dicom.viewer2d.EventManager;
import org.weasis.dicom.viewer2d.Messages;
import bibliothek.gui.dock.common.CLocation;
import it.cnr.imaa.essi.lablib.gui.checkboxtree.CheckboxTree;
import it.cnr.imaa.essi.lablib.gui.checkboxtree.DefaultCheckboxTreeCellRenderer;
import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingEvent;
import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingModel;
import it.cnr.imaa.essi.lablib.gui.checkboxtree.TreeCheckingModel.CheckingMode;
@SuppressWarnings("serial")
public class DisplayTool extends PluginTool implements SeriesViewerListener {
public static final String IMAGE = Messages.getString("DisplayTool.image"); //$NON-NLS-1$
public static final String DICOM_IMAGE_OVERLAY = Messages.getString("DisplayTool.dicom_overlay"); //$NON-NLS-1$
public static final String DICOM_PIXEL_PADDING = Messages.getString("DisplayTool.pixpad"); //$NON-NLS-1$
public static final String DICOM_SHUTTER = Messages.getString("DisplayTool.shutter"); //$NON-NLS-1$
public static final String DICOM_ANNOTATIONS = Messages.getString("DisplayTool.dicom_ano"); //$NON-NLS-1$
public static final String BUTTON_NAME = Messages.getString("DisplayTool.display"); //$NON-NLS-1$
private final JCheckBox applyAllViews = new JCheckBox(Messages.getString("DisplayTool.btn_apply_all"), true); //$NON-NLS-1$
private final CheckboxTree tree;
private boolean initPathSelection;
private DefaultMutableTreeNode rootNode = new DefaultMutableTreeNode("rootNode", true); //$NON-NLS-1$
private DefaultMutableTreeNode imageNode;
private DefaultMutableTreeNode dicomInfo;
private DefaultMutableTreeNode drawings;
private DefaultMutableTreeNode crosslines;
private DefaultMutableTreeNode minAnnotations;
private TreePath rootPath;
private JPanel panelFoot;
public DisplayTool(String pluginName) {
super(BUTTON_NAME, pluginName, PluginTool.Type.TOOL, 10);
dockable.setTitleIcon(new ImageIcon(DisplayTool.class.getResource("/icon/16x16/display.png"))); //$NON-NLS-1$
setDockableWidth(210);
tree = new CheckboxTree();
initPathSelection = false;
setLayout(new BorderLayout(0, 0));
iniTree();
}
public void iniTree() {
tree.getCheckingModel().setCheckingMode(CheckingMode.SIMPLE);
imageNode = new DefaultMutableTreeNode(IMAGE, true);
imageNode.add(new DefaultMutableTreeNode(DICOM_IMAGE_OVERLAY, false));
imageNode.add(new DefaultMutableTreeNode(DICOM_SHUTTER, false));
imageNode.add(new DefaultMutableTreeNode(DICOM_PIXEL_PADDING, false));
rootNode.add(imageNode);
dicomInfo = new DefaultMutableTreeNode(DICOM_ANNOTATIONS, true);
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.ANNOTATIONS, true));
minAnnotations = new DefaultMutableTreeNode(LayerAnnotation.MIN_ANNOTATIONS, false);
dicomInfo.add(minAnnotations);
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.ANONYM_ANNOTATIONS, false));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.SCALE, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.LUT, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.IMAGE_ORIENTATION, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.WINDOW_LEVEL, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.ZOOM, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.ROTATION, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.FRAME, true));
dicomInfo.add(new DefaultMutableTreeNode(LayerAnnotation.PIXEL, true));
rootNode.add(dicomInfo);
drawings = new DefaultMutableTreeNode(ActionW.DRAWINGS, true);
rootNode.add(drawings);
crosslines = new DefaultMutableTreeNode(LayerType.CROSSLINES, false);
drawings.add(crosslines);
DefaultTreeModel model = new DefaultTreeModel(rootNode, false);
tree.setModel(model);
rootPath = new TreePath(rootNode.getPath());
tree.addCheckingPath(rootPath);
tree.setShowsRootHandles(true);
tree.setRootVisible(false);
tree.setExpandsSelectedPaths(true);
DefaultCheckboxTreeCellRenderer renderer = new DefaultCheckboxTreeCellRenderer();
renderer.setOpenIcon(null);
renderer.setClosedIcon(null);
renderer.setLeafIcon(null);
tree.setCellRenderer(renderer);
tree.addTreeCheckingListener(this::treeValueChanged);
JPanel panel = new JPanel();
FlowLayout flowLayout = (FlowLayout) panel.getLayout();
flowLayout.setAlignment(FlowLayout.LEFT);
add(panel, BorderLayout.NORTH);
panel.add(applyAllViews);
expandTree(tree, rootNode);
add(new JScrollPane(tree), BorderLayout.CENTER);
panelFoot = new JPanel();
// To handle selection color with all L&Fs
panelFoot.setUI(new javax.swing.plaf.PanelUI() {
});
panelFoot.setOpaque(true);
panelFoot.setBackground(JMVUtils.TREE_BACKROUND);
add(panelFoot, BorderLayout.SOUTH);
}
private void treeValueChanged(TreeCheckingEvent e) {
if (!initPathSelection) {
TreePath path = e.getPath();
boolean selected = e.isCheckedPath();
Object selObject = path.getLastPathComponent();
Object parent = null;
if (path.getParentPath() != null) {
parent = path.getParentPath().getLastPathComponent();
}
ImageViewerPlugin<DicomImageElement> container = EventManager.getInstance().getSelectedView2dContainer();
List<ViewCanvas<DicomImageElement>> views = null;
if (container != null) {
if (applyAllViews.isSelected()) {
views = container.getImagePanels();
} else {
views = new ArrayList<>(1);
Optional.ofNullable(container.getSelectedImagePane()).ifPresent(views::add);
}
}
if (views != null) {
if (rootNode.equals(parent)) {
if (imageNode.equals(selObject)) {
for (ViewCanvas<DicomImageElement> v : views) {
if (selected != v.getImageLayer().getVisible()) {
v.getImageLayer().setVisible(selected);
v.getJComponent().repaint();
}
}
} else if (dicomInfo.equals(selObject)) {
for (ViewCanvas<DicomImageElement> v : views) {
LayerAnnotation layer = v.getInfoLayer();
if (layer != null) {
if (selected != v.getInfoLayer().getVisible()) {
v.getInfoLayer().setVisible(selected);
v.getJComponent().repaint();
}
}
}
} else if (drawings.equals(selObject)) {
for (ViewCanvas<DicomImageElement> v : views) {
v.setDrawingsVisibility(selected);
}
}
} else if (imageNode.equals(parent)) {
if (selObject != null) {
if (DICOM_IMAGE_OVERLAY.equals(selObject.toString())) {
sendPropertyChangeEvent(views, ActionW.IMAGE_OVERLAY.cmd(), selected);
} else if (DICOM_SHUTTER.equals(selObject.toString())) {
sendPropertyChangeEvent(views, ActionW.IMAGE_SHUTTER.cmd(), selected);
} else if (DICOM_PIXEL_PADDING.equals(selObject.toString())) {
sendPropertyChangeEvent(views, ActionW.IMAGE_PIX_PADDING.cmd(), selected);
}
}
} else if (dicomInfo.equals(parent)) {
if (selObject != null) {
for (ViewCanvas<DicomImageElement> v : views) {
LayerAnnotation layer = v.getInfoLayer();
if (layer != null) {
if (layer.setDisplayPreferencesValue(selObject.toString(), selected)) {
v.getJComponent().repaint();
}
}
}
if (LayerAnnotation.ANONYM_ANNOTATIONS.equals(selObject.toString())) {
// Send message to listeners, only selected view
ViewCanvas<DicomImageElement> v = container.getSelectedImagePane();
Series<?> series = (Series<?>) v.getSeries();
EventManager.getInstance().fireSeriesViewerListeners(
new SeriesViewerEvent(container, series, v.getImage(), EVENT.ANONYM));
}
}
} else if (drawings.equals(parent)) {
if (selObject == crosslines) {
for (ViewCanvas<DicomImageElement> v : views) {
if ((Boolean) v.getActionValue(LayerType.CROSSLINES.name()) != selected) {
v.setActionsInView(LayerType.CROSSLINES.name(), selected);
if (!selected) {
v.getGraphicManager().deleteByLayerType(LayerType.CROSSLINES);
v.getJComponent().repaint();
} else {
// Force to redraw crosslines
Optional
.ofNullable(
(SliderChangeListener) v.getEventManager().getAction(ActionW.SCROLL_SERIES))
.ifPresent(a -> a.stateChanged(a.getSliderModel()));
}
}
}
}
}
}
}
}
private static void sendPropertyChangeEvent(List<ViewCanvas<DicomImageElement>> views, String cmd,
boolean selected) {
for (ViewCanvas<DicomImageElement> v : views) {
v.propertyChange(new PropertyChangeEvent(EventManager.getInstance(), cmd, null, selected));
}
}
private void iniDicomView(OpManager disOp, String op, String param, int index) {
TreeNode treeNode = imageNode.getChildAt(index);
if (treeNode != null) {
Boolean val = (Boolean) disOp.getParamValue(op, param);
initPathSelection(getTreePath(treeNode), val == null ? false : val);
}
}
private void initPathSelection(TreePath path, boolean selected) {
if (selected) {
tree.addCheckingPath(path);
} else {
tree.removeCheckingPath(path);
}
}
public void iniTreeValues(ViewCanvas<?> view) {
if (view != null) {
initPathSelection = true;
// Image node
OpManager disOp = view.getDisplayOpManager();
initPathSelection(getTreePath(imageNode), view.getImageLayer().getVisible());
iniDicomView(disOp, OverlayOp.OP_NAME, OverlayOp.P_SHOW, 0);
iniDicomView(disOp, ShutterOp.OP_NAME, ShutterOp.P_SHOW, 1);
iniDicomView(disOp, WindowOp.OP_NAME, ActionW.IMAGE_PIX_PADDING.cmd(), 2);
// Annotations node
LayerAnnotation layer = view.getInfoLayer();
if (layer != null) {
initPathSelection(getTreePath(dicomInfo), layer.getVisible());
Enumeration<?> en = dicomInfo.children();
while (en.hasMoreElements()) {
Object node = en.nextElement();
if (node instanceof TreeNode) {
TreeNode checkNode = (TreeNode) node;
initPathSelection(getTreePath(checkNode), layer.getDisplayPreferences(node.toString()));
}
}
}
initLayers(view);
ImageElement img = view.getImage();
if (img != null) {
Panner<?> panner = view.getPanner();
if (panner != null) {
// int cps = panelFoot.getComponentCount();
// if (cps > 0) {
// Component cp = panelFoot.getComponent(0);
// if (cp != panner) {
// if (cp instanceof Thumbnail) {
// ((Thumbnail) cp).removeMouseAndKeyListener();
// }
// panner.registerListeners();
// panelFoot.removeAll();
// panelFoot.add(panner);
// panner.revalidate();
// panner.repaint();
// }
// } else {
// panner.registerListeners();
// panelFoot.add(panner);
// }
}
}
initPathSelection = false;
}
}
private void initLayers(ViewCanvas<?> view) {
initPathSelection(getTreePath(drawings), JMVUtils.getNULLtoTrue(view.getActionValue(ActionW.DRAWINGS.cmd())));
initPathSelection(getTreePath(crosslines),
JMVUtils.getNULLtoTrue(view.getActionValue(LayerType.CROSSLINES.name())));
}
private static TreePath getTreePath(TreeNode node) {
List<TreeNode> list = new ArrayList<>();
list.add(node);
TreeNode parent = node;
while (parent.getParent() != null) {
parent = parent.getParent();
list.add(parent);
}
Collections.reverse(list);
return new TreePath(list.toArray(new TreeNode[list.size()]));
}
@Override
public Component getToolComponent() {
return this;
}
@Override
protected void changeToolWindowAnchor(CLocation clocation) {
// TODO Auto-generated method stub
}
@Override
public void changingViewContentEvent(SeriesViewerEvent event) {
// TODO should recieved layer changes
EVENT e = event.getEventType();
if (EVENT.SELECT_VIEW.equals(e) && event.getSeriesViewer() instanceof ImageViewerPlugin) {
iniTreeValues(((ImageViewerPlugin<?>) event.getSeriesViewer()).getSelectedImagePane());
} else if (EVENT.TOOGLE_INFO.equals(e)) {
TreeCheckingModel model = tree.getCheckingModel();
TreePath path = new TreePath(dicomInfo.getPath());
boolean checked = model.isPathChecked(path);
ViewCanvas<DicomImageElement> selView = EventManager.getInstance().getSelectedViewPane();
// Use an intermediate state of the minimal DICOM information. Triggered only from the shortcut SPACE or I
boolean minDisp =
selView != null && selView.getInfoLayer().getDisplayPreferences(LayerAnnotation.MIN_ANNOTATIONS);
if (checked && !minDisp) {
ImageViewerPlugin<DicomImageElement> container =
EventManager.getInstance().getSelectedView2dContainer();
List<ViewCanvas<DicomImageElement>> views = null;
if (container != null) {
if (applyAllViews.isSelected()) {
views = container.getImagePanels();
} else {
views = new ArrayList<>(1);
ViewCanvas<DicomImageElement> view = container.getSelectedImagePane();
if (view != null) {
views.add(view);
}
}
}
if (views != null) {
for (ViewCanvas<DicomImageElement> v : views) {
LayerAnnotation layer = v.getInfoLayer();
if (layer != null) {
layer.setVisible(true);
if (layer.setDisplayPreferencesValue(LayerAnnotation.MIN_ANNOTATIONS, true)) {
v.getJComponent().repaint();
}
}
}
}
model.addCheckingPath(new TreePath(minAnnotations.getPath()));
} else if (checked) {
model.removeCheckingPath(path);
model.removeCheckingPath(new TreePath(minAnnotations.getPath()));
} else {
model.addCheckingPath(path);
model.removeCheckingPath(new TreePath(minAnnotations.getPath()));
}
}
}
private static void expandTree(JTree tree, DefaultMutableTreeNode start) {
Enumeration<?> children = start.children();
while (children.hasMoreElements()) {
Object child = children.nextElement();
if (child instanceof DefaultMutableTreeNode) {
DefaultMutableTreeNode dtm = (DefaultMutableTreeNode) child;
if (!dtm.isLeaf()) {
TreePath tp = new TreePath(dtm.getPath());
tree.expandPath(tp);
expandTree(tree, dtm);
}
}
}
return;
}
}