/*******************************************************************************
* 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;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.image.DataBuffer;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.stream.Collectors;
import javax.swing.BoundedRangeModel;
import javax.swing.ButtonGroup;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JSeparator;
import javax.swing.KeyStroke;
import javax.swing.SwingUtilities;
import org.dcm4che3.data.Tag;
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.core.api.command.Option;
import org.weasis.core.api.command.Options;
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.BasicActionState;
import org.weasis.core.api.gui.util.ComboItemListener;
import org.weasis.core.api.gui.util.Filter;
import org.weasis.core.api.gui.util.GuiExecutor;
import org.weasis.core.api.gui.util.JMVUtils;
import org.weasis.core.api.gui.util.RadioMenuItem;
import org.weasis.core.api.gui.util.SliderChangeListener;
import org.weasis.core.api.gui.util.SliderCineListener;
import org.weasis.core.api.gui.util.SliderCineListener.TIME;
import org.weasis.core.api.gui.util.ToggleButtonListener;
import org.weasis.core.api.image.FilterOp;
import org.weasis.core.api.image.FlipOp;
import org.weasis.core.api.image.GridBagLayoutModel;
import org.weasis.core.api.image.ImageOpNode;
import org.weasis.core.api.image.LutShape;
import org.weasis.core.api.image.OpManager;
import org.weasis.core.api.image.PseudoColorOp;
import org.weasis.core.api.image.RotationOp;
import org.weasis.core.api.image.WindowOp;
import org.weasis.core.api.image.op.ByteLut;
import org.weasis.core.api.image.op.ByteLutCollection;
import org.weasis.core.api.image.util.KernelData;
import org.weasis.core.api.image.util.Unit;
import org.weasis.core.api.media.data.MediaSeries;
import org.weasis.core.api.media.data.MediaSeries.MEDIA_POSITION;
import org.weasis.core.api.media.data.Series;
import org.weasis.core.api.media.data.SeriesComparator;
import org.weasis.core.api.media.data.TagW;
import org.weasis.core.api.service.AuditLog;
import org.weasis.core.api.service.BundlePreferences;
import org.weasis.core.api.service.BundleTools;
import org.weasis.core.api.service.WProperties;
import org.weasis.core.api.util.ResourceUtil;
import org.weasis.core.ui.docking.DockableTool;
import org.weasis.core.ui.editor.SeriesViewerEvent;
import org.weasis.core.ui.editor.SeriesViewerEvent.EVENT;
import org.weasis.core.ui.editor.image.DefaultView2d;
import org.weasis.core.ui.editor.image.ImageViewerEventManager;
import org.weasis.core.ui.editor.image.ImageViewerPlugin;
import org.weasis.core.ui.editor.image.MeasureToolBar;
import org.weasis.core.ui.editor.image.MouseActions;
import org.weasis.core.ui.editor.image.PannerListener;
import org.weasis.core.ui.editor.image.SynchCineEvent;
import org.weasis.core.ui.editor.image.SynchData;
import org.weasis.core.ui.editor.image.SynchData.Mode;
import org.weasis.core.ui.editor.image.SynchEvent;
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.model.graphic.Graphic;
import org.weasis.core.ui.model.graphic.GraphicSelectionListener;
import org.weasis.core.ui.model.layer.LayerType;
import org.weasis.core.ui.model.utils.bean.PanPoint;
import org.weasis.core.ui.util.ColorLayerUI;
import org.weasis.core.ui.util.PrintDialog;
import org.weasis.dicom.codec.DicomImageElement;
import org.weasis.dicom.codec.DicomSeries;
import org.weasis.dicom.codec.PresentationStateReader;
import org.weasis.dicom.codec.SortSeriesStack;
import org.weasis.dicom.codec.TagD;
import org.weasis.dicom.codec.display.PresetWindowLevel;
import org.weasis.dicom.codec.geometry.ImageOrientation;
import org.weasis.dicom.viewer2d.mip.MipView;
import org.weasis.dicom.viewer2d.mpr.MPRContainer;
import org.weasis.dicom.viewer2d.mpr.MprView;
/**
* The event processing center for this application. This class responses for loading data sets, processing the events
* from the utility menu that includes changing the operation scope, the layout, window/level, rotation angle, zoom
* factor, starting/stoping the cining-loop and etc.
*
*/
public class EventManager extends ImageViewerEventManager<DicomImageElement> implements ActionListener {
private static final Logger LOGGER = LoggerFactory.getLogger(EventManager.class);
public static final String[] functions =
{ "zoom", "wl", "move", "scroll", "layout", "mouseLeftAction", "synch", "reset" }; //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ //$NON-NLS-5$ //$NON-NLS-6$ //$NON-NLS-7$ //$NON-NLS-8$
/** The single instance of this singleton class. */
private static EventManager instance;
/**
* Return the single instance of this class. This method guarantees the singleton property of this class.
*/
public static synchronized EventManager getInstance() {
if (instance == null) {
instance = new EventManager();
}
return instance;
}
/**
* The default private constructor to guarantee the singleton property of this class.
*/
private EventManager() {
// Initialize actions with a null value. These are used by mouse or keyevent actions.
setAction(new BasicActionState(ActionW.WINLEVEL));
setAction(new BasicActionState(ActionW.CONTEXTMENU));
setAction(new BasicActionState(ActionW.NO_ACTION));
setAction(new BasicActionState(ActionW.DRAW));
setAction(new BasicActionState(ActionW.MEASURE));
setAction(getMoveTroughSliceAction(20, TIME.SECOND, 0.1));
setAction(newWindowAction());
setAction(newLevelAction());
setAction(newRotateAction());
setAction(newZoomAction());
setAction(newFlipAction());
setAction(newInverseLutAction());
setAction(newInverseStackAction());
setAction(newLensAction());
setAction(newLensZoomAction());
setAction(newDrawOnlyOnceAction());
setAction(newDefaulPresetAction());
setAction(newPresetAction());
setAction(newLutShapeAction());
setAction(newLutAction());
setAction(newFilterAction());
setAction(newSortStackAction());
setAction(newLayoutAction(
View2dContainer.LAYOUT_LIST.toArray(new GridBagLayoutModel[View2dContainer.LAYOUT_LIST.size()])));
setAction(newSynchAction(View2dContainer.SYNCH_LIST.toArray(new SynchView[View2dContainer.SYNCH_LIST.size()])));
getAction(ActionW.SYNCH, ComboItemListener.class)
.ifPresent(a -> a.setSelectedItemWithoutTriggerAction(SynchView.DEFAULT_STACK));
setAction(newMeasurementAction(
MeasureToolBar.measureGraphicList.toArray(new Graphic[MeasureToolBar.measureGraphicList.size()])));
setAction(
newDrawAction(MeasureToolBar.drawGraphicList.toArray(new Graphic[MeasureToolBar.drawGraphicList.size()])));
setAction(newSpatialUnit(Unit.values()));
setAction(newPanAction());
setAction(newCrosshairAction());
setAction(new BasicActionState(ActionW.RESET));
setAction(new BasicActionState(ActionW.SHOW_HEADER));
setAction(newKOToggleAction());
setAction(newKOFilterAction());
setAction(newKOSelectionAction());
final BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext();
Preferences prefs = BundlePreferences.getDefaultPreferences(context);
zoomSetting.applyPreferences(prefs);
mouseActions.applyPreferences(prefs);
if (prefs != null) {
Preferences prefNode = prefs.node("mouse.sensivity"); //$NON-NLS-1$
getSliderPreference(prefNode, ActionW.WINDOW, 1.25);
getSliderPreference(prefNode, ActionW.LEVEL, 1.25);
getSliderPreference(prefNode, ActionW.SCROLL_SERIES, 0.1);
getSliderPreference(prefNode, ActionW.ROTATION, 0.25);
getSliderPreference(prefNode, ActionW.ZOOM, 0.1);
/*
* Get first the local value if exist, otherwise try to get the default server configuration and finally if
* no value take the default value in parameter.
*/
prefNode = prefs.node("other"); //$NON-NLS-1$
WProperties.setProperty(options, WindowOp.P_APPLY_WL_COLOR, prefNode, Boolean.TRUE.toString());
}
initializeParameters();
}
private void initializeParameters() {
enableActions(false);
}
private ComboItemListener<KernelData> newFilterAction() {
return new ComboItemListener<KernelData>(ActionW.FILTER, KernelData.getAllFilters()) {
@Override
public void itemStateChanged(Object object) {
if (object instanceof KernelData) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), action.cmd(), object));
}
}
};
}
@Override
protected SliderCineListener getMoveTroughSliceAction(int speed, TIME time, double mouseSensivity) {
return new SliderCineListener(ActionW.SCROLL_SERIES, 1, 2, 1, speed, time, mouseSensivity) {
protected CineThread currentCine;
@Override
public void stateChanged(BoundedRangeModel model) {
ViewCanvas<DicomImageElement> view2d = null;
Series<DicomImageElement> series = null;
SynchCineEvent mediaEvent = null;
DicomImageElement image = null;
Optional<ToggleButtonListener> defaultPresetAction =
getAction(ActionW.DEFAULT_PRESET, ToggleButtonListener.class);
boolean isDefaultPresetSelected =
defaultPresetAction.isPresent() ? defaultPresetAction.get().isSelected() : true;
if (selectedView2dContainer != null) {
view2d = selectedView2dContainer.getSelectedImagePane();
}
if (view2d != null && view2d.getSeries() instanceof Series) {
series = (Series<DicomImageElement>) view2d.getSeries();
if (series != null) {
// Model contains display value, value-1 is the index value of a sequence
int index = model.getValue() - 1;
image = series.getMedia(index,
(Filter<DicomImageElement>) view2d.getActionValue(ActionW.FILTERED_SERIES.cmd()),
view2d.getCurrentSortComparator());
mediaEvent = new SynchCineEvent(view2d, image, index);
// Ensure to load image before calling the default preset (requires pixel min and max)
if (image != null && !image.isImageAvailable()) {
image.getImage();
}
}
}
Optional<ComboItemListener> layoutAction = getAction(ActionW.LAYOUT, ComboItemListener.class);
Optional<ComboItemListener> synchAction = getAction(ActionW.SYNCH, ComboItemListener.class);
if (image != null && layoutAction.isPresent() && View2dFactory
.getViewTypeNumber((GridBagLayoutModel) layoutAction.get().getSelectedItem(), ViewCanvas.class) > 1
&& synchAction.isPresent()) {
SynchView synchview = (SynchView) synchAction.get().getSelectedItem();
if (synchview.getSynchData().isActionEnable(ActionW.SCROLL_SERIES.cmd())) {
double[] val = (double[]) image.getTagValue(TagW.SlicePosition);
if (val != null) {
mediaEvent.setLocation(val[0] + val[1] + val[2]);
}
} else {
if (selectedView2dContainer != null) {
final List<ViewCanvas<DicomImageElement>> panes = selectedView2dContainer.getImagePanels();
for (ViewCanvas<DicomImageElement> p : panes) {
Boolean cutlines = (Boolean) p.getActionValue(ActionW.SYNCH_CROSSLINE.cmd());
if (cutlines != null && cutlines) {
double[] val = (double[]) image.getTagValue(TagW.SlicePosition);
if (val != null) {
mediaEvent.setLocation(val[0] + val[1] + val[2]);
} else {
return; // Do not throw event
}
break;
}
}
}
}
}
Optional<SliderChangeListener> windowAction = getAction(ActionW.WINDOW, SliderChangeListener.class);
Optional<SliderChangeListener> levelAction = getAction(ActionW.LEVEL, SliderChangeListener.class);
if (view2d != null && image != view2d.getImage() && image != null && windowAction.isPresent()
&& levelAction.isPresent()) {
Optional<ComboItemListener> presetAction = getAction(ActionW.PRESET, ComboItemListener.class);
PresetWindowLevel oldPreset =
presetAction.isPresent() ? (PresetWindowLevel) presetAction.get().getSelectedItem() : null;
PresetWindowLevel newPreset = null;
boolean pixelPadding = JMVUtils.getNULLtoTrue(
view2d.getDisplayOpManager().getParamValue(WindowOp.OP_NAME, ActionW.IMAGE_PIX_PADDING.cmd()));
List<PresetWindowLevel> newPresetList = image.getPresetList(pixelPadding);
// Assume the image cannot display when win =1 and level = 0
if (oldPreset != null
|| (windowAction.get().getSliderValue() <= 1 && levelAction.get().getSliderValue() == 0)) {
if (isDefaultPresetSelected) {
newPreset = image.getDefaultPreset(pixelPadding);
} else {
if (oldPreset != null) {
for (PresetWindowLevel preset : newPresetList) {
if (preset.getName().equals(oldPreset.getName())) {
newPreset = preset;
break;
}
}
}
// set default preset when the old preset is not available any more
if (newPreset == null) {
newPreset = image.getDefaultPreset(pixelPadding);
isDefaultPresetSelected = true;
}
}
}
Optional<ComboItemListener> lutShapeAction = getAction(ActionW.LUT_SHAPE, ComboItemListener.class);
Double windowValue = newPreset == null ? windowAction.get().getRealValue() : newPreset.getWindow();
Double levelValue = newPreset == null ? levelAction.get().getRealValue() : newPreset.getLevel();
LutShape lutShapeItem = newPreset == null
? lutShapeAction.isPresent() ? (LutShape) lutShapeAction.get().getSelectedItem() : null
: newPreset.getLutShape();
Double levelMin =
(Double) view2d.getDisplayOpManager().getParamValue(WindowOp.OP_NAME, ActionW.LEVEL_MIN.cmd());
Double levelMax =
(Double) view2d.getDisplayOpManager().getParamValue(WindowOp.OP_NAME, ActionW.LEVEL_MAX.cmd());
PresentationStateReader prReader =
(PresentationStateReader) view2d.getActionValue(PresentationStateReader.TAG_PR_READER);
if (levelMin == null || levelMax == null) {
levelMin = Math.min(levelValue - windowValue / 2.0, image.getMinValue(prReader, pixelPadding));
levelMax = Math.max(levelValue + windowValue / 2.0, image.getMaxValue(prReader, pixelPadding));
} else {
levelMin = Math.min(levelMin, image.getMinValue(prReader, pixelPadding));
levelMax = Math.max(levelMax, image.getMaxValue(prReader, pixelPadding));
}
// FIX : setting actionInView here without firing a propertyChange avoid another call to
// imageLayer.updateImageOperation(WindowOp.name.....
// TODO pass to mediaEvent with PR and KO
ImageOpNode node = view2d.getDisplayOpManager().getNode(WindowOp.OP_NAME);
if (node != null) {
node.setParam(ActionW.PRESET.cmd(), newPreset);
node.setParam(ActionW.DEFAULT_PRESET.cmd(), isDefaultPresetSelected);
node.setParam(ActionW.WINDOW.cmd(), windowValue);
node.setParam(ActionW.LEVEL.cmd(), levelValue);
node.setParam(ActionW.LEVEL_MIN.cmd(), levelMin);
node.setParam(ActionW.LEVEL_MAX.cmd(), levelMax);
node.setParam(ActionW.LUT_SHAPE.cmd(), lutShapeItem);
}
updateWindowLevelComponentsListener(image, view2d);
}
firePropertyChange(ActionW.SYNCH.cmd(), null, mediaEvent);
if (image != null) {
fireSeriesViewerListeners(
new SeriesViewerEvent(selectedView2dContainer, series, image, EVENT.SELECT));
}
updateKeyObjectComponentsListener(view2d);
}
@Override
public void setSpeed(int speed) {
super.setSpeed(speed);
if (currentCine != null) {
currentCine.iniSpeed();
}
}
/** Create a thread to cine the images. */
class CineThread extends Thread {
private volatile int iteration;
private volatile int waitTimeMillis;
private volatile int currentCineRate;
private volatile long start;
private volatile boolean cining = true;
@Override
public void run() {
iniSpeed();
while (cining) {
long startFrameTime = System.currentTimeMillis();
// Set the value to SliderCineListener, must be in EDT for refreshing UI correctly
GuiExecutor.instance().invokeAndWait(new Runnable() {
@Override
public void run() {
if (cining) {
int frameIndex = getSliderValue() + 1;
setSliderValue(frameIndex > getSliderMax() ? 0 : frameIndex);
}
}
});
// Time to set the new frame index
long elapsedFrame = System.currentTimeMillis() - startFrameTime;
/*
* If this time is smaller than the time to wait according to the cine speed (fps), then wait
* the time left, otherwise continue (that means the cine speed cannot be reached)
*/
if (elapsedFrame < waitTimeMillis) {
try {
Thread.sleep(waitTimeMillis - elapsedFrame);
} catch (Exception e) {
}
}
iteration++;
// Check the speed every 3 images
if (iteration > 2) {
// Get the speed rate (fps) on the last 3 images
currentCineRate = (int) (iteration * 1000 / (System.currentTimeMillis() - start));
// reinitialize the parameters for computing speed next time
iteration = 0;
waitTimeMillis = 1000 / getSpeed();
start = System.currentTimeMillis();
}
}
}
public void iniSpeed() {
iteration = 0;
currentCineRate = getSpeed();
waitTimeMillis = 1000 / currentCineRate;
start = System.currentTimeMillis();
}
public int getCurrentCineRate() {
return currentCineRate;
}
}
/** Start the cining. */
@Override
public synchronized void start() {
if (currentCine != null) {
stop();
}
if (getSliderMax() - getSliderMin() > 0) {
currentCine = new CineThread();
currentCine.start();
}
}
/** Stop the cining. */
@Override
public synchronized void stop() {
CineThread moribund = currentCine;
currentCine = null;
if (moribund != null) {
moribund.cining = false;
moribund.interrupt();
}
}
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
if (isActionEnabled()) {
setSliderValue(getSliderValue() + e.getWheelRotation());
}
}
@Override
public int getCurrentCineRate() {
if (currentCine != null) {
return currentCine.getCurrentCineRate();
}
return 0;
}
@Override
public boolean isCining() {
return currentCine != null;
}
};
}
@Override
protected SliderChangeListener newWindowAction() {
return new SliderChangeListener(ActionW.WINDOW, WINDOW_SMALLEST, WINDOW_LARGEST, WINDOW_DEFAULT, true, 1.25) {
@Override
public void stateChanged(BoundedRangeModel model) {
updatePreset(getActionW().cmd(), toModelValue(model.getValue()));
}
};
}
@Override
protected SliderChangeListener newLevelAction() {
return new SliderChangeListener(ActionW.LEVEL, LEVEL_SMALLEST, LEVEL_LARGEST, LEVEL_DEFAULT, true, 1.25) {
@Override
public void stateChanged(BoundedRangeModel model) {
updatePreset(getActionW().cmd(), toModelValue(model.getValue()));
}
};
}
protected void updatePreset(String cmd, Object object) {
String command = cmd;
boolean isDefaultPresetSelected = false;
Optional<ComboItemListener> presetAction = getAction(ActionW.PRESET, ComboItemListener.class);
if (ActionW.PRESET.cmd().equals(command) && object instanceof PresetWindowLevel) {
PresetWindowLevel preset = (PresetWindowLevel) object;
getAction(ActionW.WINDOW, SliderChangeListener.class)
.ifPresent(a -> a.setSliderValue(a.toSliderValue(preset.getWindow()), false));
getAction(ActionW.LEVEL, SliderChangeListener.class)
.ifPresent(a -> a.setSliderValue(a.toSliderValue(preset.getLevel()), false));
getAction(ActionW.LUT_SHAPE, ComboItemListener.class)
.ifPresent(a -> a.setSelectedItemWithoutTriggerAction(preset.getLutShape()));
PresetWindowLevel defaultPreset =
presetAction.isPresent() ? (PresetWindowLevel) presetAction.get().getFirstItem() : null;
isDefaultPresetSelected = defaultPreset == null ? false : preset.equals(defaultPreset);
} else {
presetAction.ifPresent(
a -> a.setSelectedItemWithoutTriggerAction(object instanceof PresetWindowLevel ? object : null));
}
Optional<ToggleButtonListener> defaultPresetAction =
getAction(ActionW.DEFAULT_PRESET, ToggleButtonListener.class);
if (defaultPresetAction.isPresent()) {
defaultPresetAction.get().setSelectedWithoutTriggerAction(isDefaultPresetSelected);
SynchEvent evt = new SynchEvent(getSelectedViewPane(), ActionW.DEFAULT_PRESET.cmd(),
defaultPresetAction.get().isSelected());
evt.put(command, object);
firePropertyChange(ActionW.SYNCH.cmd(), null, evt);
}
}
private ComboItemListener<PresetWindowLevel> newPresetAction() {
return new ComboItemListener<PresetWindowLevel>(ActionW.PRESET, null) {
@Override
public void itemStateChanged(Object object) {
updatePreset(getActionW().cmd(), object);
}
};
}
private ComboItemListener<LutShape> newLutShapeAction() {
return new ComboItemListener<LutShape>(ActionW.LUT_SHAPE,
LutShape.DEFAULT_FACTORY_FUNCTIONS.toArray(new LutShape[LutShape.DEFAULT_FACTORY_FUNCTIONS.size()])) {
@Override
public void itemStateChanged(Object object) {
updatePreset(action.cmd(), object);
}
};
}
private ToggleButtonListener newDefaulPresetAction() {
return new ToggleButtonListener(ActionW.DEFAULT_PRESET, true) {
@Override
public void actionPerformed(boolean selected) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), action.cmd(), selected));
}
};
}
private ToggleButtonListener newKOToggleAction() {
return new ToggleButtonListener(ActionW.KO_TOOGLE_STATE, false) {
@Override
public void actionPerformed(boolean newSelectedState) {
boolean hasKeyObjectReferenceChanged =
KOManager.setKeyObjectReference(newSelectedState, getSelectedViewPane());
if (!hasKeyObjectReferenceChanged) {
// If KO Toogle State hasn't changed this action should be reset to its previous state, that is the
// current view's actionValue
this.setSelectedWithoutTriggerAction(
(Boolean) getSelectedViewPane().getActionValue(ActionW.KO_TOOGLE_STATE.cmd()));
}
}
};
}
private ComboItemListener<Object> newKOSelectionAction() {
return new ComboItemListener<Object>(ActionW.KO_SELECTION,
new ActionState.NoneLabel[] { ActionState.NoneLabel.NONE }) {
@Override
public void itemStateChanged(Object object) {
koAction(action, object);
}
};
}
private ToggleButtonListener newKOFilterAction() {
return new ToggleButtonListener(ActionW.KO_FILTER, false) {
@Override
public void actionPerformed(boolean selected) {
koAction(action, selected);
}
};
}
private void koAction(ActionW action, Object selected) {
Optional<ComboItemListener> synchAction = getAction(ActionW.SYNCH, ComboItemListener.class);
SynchView synchView = synchAction.isPresent() ? (SynchView) synchAction.get().getSelectedItem() : null;
boolean tileMode = synchView != null && SynchData.Mode.TILE.equals(synchView.getSynchData().getMode());
ViewCanvas<DicomImageElement> selectedView = getSelectedViewPane();
if (tileMode) {
if (selectedView2dContainer instanceof View2dContainer && selectedView != null) {
View2dContainer container = (View2dContainer) selectedView2dContainer;
boolean filterSelection = selected instanceof Boolean;
Object selectedKO =
filterSelection ? selectedView.getActionValue(ActionW.KO_SELECTION.cmd()) : selected;
Boolean enableFilter =
(Boolean) (filterSelection ? selected : selectedView.getActionValue(ActionW.KO_FILTER.cmd()));
ViewCanvas<DicomImageElement> viewPane = container.getSelectedImagePane();
int frameIndex =
JMVUtils.getNULLtoFalse(enableFilter) ? 0 : viewPane.getFrameIndex() - viewPane.getTileOffset();
for (ViewCanvas<DicomImageElement> view : container.getImagePanels(true)) {
if (!(view.getSeries() instanceof DicomSeries) || !(view instanceof View2d)) {
continue;
}
KOManager.updateKOFilter(view, selectedKO, enableFilter, frameIndex, false);
}
container.updateTileOffset();
if (!(selectedView.getSeries() instanceof DicomSeries)) {
List<ViewCanvas<DicomImageElement>> panes = selectedView2dContainer.getImagePanels(false);
if (!panes.isEmpty()) {
selectedView2dContainer.setSelectedImagePane(panes.get(0));
return;
}
}
}
}
firePropertyChange(ActionW.SYNCH.cmd(), null, new SynchEvent(selectedView, action.cmd(), selected));
if (tileMode) {
Optional<SliderCineListener> cineAction = getAction(ActionW.SCROLL_SERIES, SliderCineListener.class);
if (cineAction.isPresent() && cineAction.get().isActionEnabled()) {
SliderCineListener moveTroughSliceAction = cineAction.get();
if (moveTroughSliceAction.getSliderValue() == 1) {
moveTroughSliceAction.stateChanged(moveTroughSliceAction.getSliderModel());
} else {
moveTroughSliceAction.setSliderValue(1);
}
}
}
}
private ComboItemListener<ByteLut> newLutAction() {
List<ByteLut> luts = new ArrayList<>();
luts.add(ByteLut.grayLUT);
ByteLutCollection.readLutFilesFromResourcesDir(luts, ResourceUtil.getResource("luts"));//$NON-NLS-1$
// Set default first as the list has been sorted
luts.add(0, ByteLut.defaultLUT);
return new ComboItemListener<ByteLut>(ActionW.LUT, luts.toArray(new ByteLut[luts.size()])) {
@Override
public void itemStateChanged(Object object) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), action.cmd(), object));
}
};
}
private ComboItemListener<SeriesComparator<DicomImageElement>> newSortStackAction() {
return new ComboItemListener<SeriesComparator<DicomImageElement>>(ActionW.SORTSTACK,
SortSeriesStack.getValues()) {
@Override
public void itemStateChanged(Object object) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), action.cmd(), object));
}
};
}
@Override
public Optional<ActionW> getLeftMouseActionFromkeyEvent(int keyEvent, int modifier) {
Optional<ActionW> action = super.getLeftMouseActionFromkeyEvent(keyEvent, modifier);
if (!action.isPresent()) {
return action;
}
// Only return the action if it is enabled
if (Optional.ofNullable(getAction(action.get())).filter(a -> a.isActionEnabled()).isPresent()) {
return action;
} else if (ActionW.KO_TOOGLE_STATE.equals(action.get()) && keyEvent == ActionW.KO_TOOGLE_STATE.getKeyCode()) {
Optional<ToggleButtonListener> koToggleAction =
getAction(ActionW.KO_TOOGLE_STATE, ToggleButtonListener.class);
if (koToggleAction.isPresent()) {
koToggleAction.get().setSelected(!koToggleAction.get().isSelected());
}
}
return Optional.empty();
}
@Override
public void keyTyped(KeyEvent e) {
// Do nothing
}
@Override
public void keyPressed(KeyEvent e) {
int keyEvent = e.getKeyCode();
int modifiers = e.getModifiers();
if (keyEvent == KeyEvent.VK_ESCAPE) {
resetDisplay();
} else if (keyEvent == ActionW.CINESTART.getKeyCode() && ActionW.CINESTART.getModifier() == modifiers) {
Optional<SliderCineListener> cineAction = getAction(ActionW.SCROLL_SERIES, SliderCineListener.class);
if (cineAction.isPresent() && cineAction.get().isActionEnabled()) {
if (cineAction.get().isCining()) {
cineAction.get().stop();
} else {
cineAction.get().start();
}
}
return;
} else if (keyEvent == KeyEvent.VK_P && modifiers == 0) {
ImageViewerPlugin<DicomImageElement> view = getSelectedView2dContainer();
if (view != null) {
ColorLayerUI layer = ColorLayerUI.createTransparentLayerUI(view);
PrintDialog dialog = new PrintDialog(SwingUtilities.getWindowAncestor(view),
Messages.getString("View2dContainer.print_layout"), this); //$NON-NLS-1$
ColorLayerUI.showCenterScreen(dialog, layer);
}
} else {
Optional<ComboItemListener> presetAction = getAction(ActionW.PRESET, ComboItemListener.class);
if (modifiers == 0 && presetAction.isPresent() && presetAction.get().isActionEnabled()) {
ComboItemListener presetComboListener = presetAction.get();
DefaultComboBoxModel model = presetComboListener.getModel();
for (int i = 0; i < model.getSize(); i++) {
PresetWindowLevel val = (PresetWindowLevel) model.getElementAt(i);
if (val.getKeyCode() == keyEvent) {
presetComboListener.setSelectedItem(val);
return;
}
}
}
triggerDrawingToolKeyEvent(keyEvent, modifiers);
}
}
@Override
public void keyReleased(KeyEvent e) {
// Do nothing
}
@Override
public void setSelectedView2dContainer(ImageViewerPlugin<DicomImageElement> selectedView2dContainer) {
if (this.selectedView2dContainer != null) {
this.selectedView2dContainer.setMouseActions(null);
getAction(ActionW.SCROLL_SERIES, SliderCineListener.class).ifPresent(a -> a.stop());
}
ImageViewerPlugin<DicomImageElement> oldContainer = this.selectedView2dContainer;
this.selectedView2dContainer = selectedView2dContainer;
if (selectedView2dContainer != null) {
Optional<ComboItemListener> synchAction = getAction(ActionW.SYNCH, ComboItemListener.class);
Optional<ComboItemListener> layoutAction = getAction(ActionW.LAYOUT, ComboItemListener.class);
if (oldContainer == null || !oldContainer.getClass().equals(selectedView2dContainer.getClass())) {
synchAction.ifPresent(
a -> a.setDataListWithoutTriggerAction(selectedView2dContainer.getSynchList().toArray()));
layoutAction.ifPresent(
a -> a.setDataListWithoutTriggerAction(selectedView2dContainer.getLayoutList().toArray()));
}
if (oldContainer != null) {
ViewCanvas<DicomImageElement> pane = oldContainer.getSelectedImagePane();
if (pane != null) {
pane.setFocused(false);
}
}
synchAction.ifPresent(a -> a.setSelectedItemWithoutTriggerAction(selectedView2dContainer.getSynchView()));
layoutAction.ifPresent(
a -> a.setSelectedItemWithoutTriggerAction(selectedView2dContainer.getOriginalLayoutModel()));
updateComponentsListener(selectedView2dContainer.getSelectedImagePane());
selectedView2dContainer.setMouseActions(mouseActions);
ViewCanvas<DicomImageElement> pane = selectedView2dContainer.getSelectedImagePane();
if (pane != null) {
pane.setFocused(true);
fireSeriesViewerListeners(
new SeriesViewerEvent(selectedView2dContainer, pane.getSeries(), null, EVENT.SELECT_VIEW));
}
}
}
/** process the action events. */
@Override
public void actionPerformed(ActionEvent evt) {
cinePlay(evt.getActionCommand());
}
private void cinePlay(String command) {
if (command != null) {
if (command.equals(ActionW.CINESTART.cmd())) {
getAction(ActionW.SCROLL_SERIES, SliderCineListener.class).ifPresent(a -> a.start());
} else if (command.equals(ActionW.CINESTOP.cmd())) {
getAction(ActionW.SCROLL_SERIES, SliderCineListener.class).ifPresent(a -> a.stop());
}
}
}
@Override
public void resetDisplay() {
reset(ResetTools.All);
}
public void reset(ResetTools action) {
AuditLog.LOGGER.info("reset action:{}", action.name()); //$NON-NLS-1$
if (ResetTools.All.equals(action)) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), ActionW.RESET.cmd(), true));
} else if (ResetTools.Zoom.equals(action)) {
// Pass the value 0.0 (convention: default value according the zoom type) directly to the property change,
// otherwise the value is adjusted by the BoundedRangeModel
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), ActionW.ZOOM.cmd(), 0.0));
} else if (ResetTools.Rotation.equals(action)) {
getAction(ActionW.ROTATION, SliderChangeListener.class).ifPresent(a -> a.setSliderValue(0));
} else if (ResetTools.WindowLevel.equals(action)) {
getAction(ActionW.PRESET, ComboItemListener.class).ifPresent(a -> a.setSelectedItem(a.getFirstItem()));
} else if (ResetTools.Pan.equals(action)) {
if (selectedView2dContainer != null) {
ViewCanvas viewPane = selectedView2dContainer.getSelectedImagePane();
if (viewPane != null) {
viewPane.resetPan();
}
}
}
}
@Override
public synchronized boolean updateComponentsListener(ViewCanvas<DicomImageElement> view2d) {
if (view2d == null) {
return false;
}
if (selectedView2dContainer == null || view2d != selectedView2dContainer.getSelectedImagePane()) {
return false;
}
clearAllPropertyChangeListeners();
Optional<SliderCineListener> cineAction = getAction(ActionW.SCROLL_SERIES, SliderCineListener.class);
if (view2d.getSourceImage() == null) {
enableActions(false);
if (view2d.getSeries() != null) {
// Let scrolling if only one image is corrupted in the series
cineAction.ifPresent(a -> a.enableAction(true));
}
return false;
}
if (!enabledAction) {
enableActions(true);
}
OpManager dispOp = view2d.getDisplayOpManager();
updateWindowLevelComponentsListener(view2d.getImage(), view2d);
getAction(ActionW.LUT, ComboItemListener.class).ifPresent(a -> a
.setSelectedItemWithoutTriggerAction(dispOp.getParamValue(PseudoColorOp.OP_NAME, PseudoColorOp.P_LUT)));
getAction(ActionW.INVERT_LUT, ToggleButtonListener.class).ifPresent(a -> a.setSelectedWithoutTriggerAction(
(Boolean) dispOp.getParamValue(PseudoColorOp.OP_NAME, PseudoColorOp.P_LUT_INVERSE)));
getAction(ActionW.FILTER, ComboItemListener.class).ifPresent(
a -> a.setSelectedItemWithoutTriggerAction(dispOp.getParamValue(FilterOp.OP_NAME, FilterOp.P_KERNEL_DATA)));
getAction(ActionW.ROTATION, SliderChangeListener.class).ifPresent(
a -> a.setSliderValue((Integer) dispOp.getParamValue(RotationOp.OP_NAME, RotationOp.P_ROTATE), false));
getAction(ActionW.FLIP, ToggleButtonListener.class).ifPresent(
a -> a.setSelectedWithoutTriggerAction((Boolean) dispOp.getParamValue(FlipOp.OP_NAME, FlipOp.P_FLIP)));
getAction(ActionW.ZOOM, SliderChangeListener.class)
.ifPresent(a -> a.setRealValue(Math.abs((Double) view2d.getActionValue(ActionW.ZOOM.cmd())), false));
getAction(ActionW.SPATIAL_UNIT, ComboItemListener.class)
.ifPresent(a -> a.setSelectedItemWithoutTriggerAction(view2d.getActionValue(ActionW.SPATIAL_UNIT.cmd())));
getAction(ActionW.LENS, ToggleButtonListener.class)
.ifPresent(a -> a.setSelectedWithoutTriggerAction((Boolean) view2d.getActionValue(ActionW.LENS.cmd())));
Double lensZoom = (Double) view2d.getLensActionValue(ActionW.ZOOM.cmd());
if (lensZoom != null) {
getAction(ActionW.LENSZOOM, SliderChangeListener.class)
.ifPresent(a -> a.setRealValue(Math.abs(lensZoom), false));
}
MediaSeries<DicomImageElement> series = view2d.getSeries();
cineAction.ifPresent(a -> a.setSliderMinMaxValue(1,
series.size((Filter<DicomImageElement>) view2d.getActionValue(ActionW.FILTERED_SERIES.cmd())),
view2d.getFrameIndex() + 1, false));
Integer cineRate = TagD.getTagValue(series, Tag.CineRate, Integer.class);
final Integer speed =
cineRate == null ? TagD.getTagValue(series, Tag.RecommendedDisplayFrameRate, Integer.class) : cineRate;
if (speed != null) {
cineAction.ifPresent(a -> a.setSpeed(speed));
}
getAction(ActionW.SORTSTACK, ComboItemListener.class)
.ifPresent(a -> a.setSelectedItemWithoutTriggerAction(view2d.getActionValue(ActionW.SORTSTACK.cmd())));
getAction(ActionW.INVERSESTACK, ToggleButtonListener.class).ifPresent(
a -> a.setSelectedWithoutTriggerAction((Boolean) view2d.getActionValue(ActionW.INVERSESTACK.cmd())));
updateKeyObjectComponentsListener(view2d);
// register all actions for the selected view and for the other views register according to synchview.
ComboItemListener synchAtction = getAction(ActionW.SYNCH, ComboItemListener.class).orElse(null);
updateAllListeners(selectedView2dContainer,
synchAtction == null ? SynchView.NONE : (SynchView) synchAtction.getSelectedItem());
List<DockableTool> tools = selectedView2dContainer.getToolPanel();
synchronized (tools) {
for (DockableTool p : tools) {
if (p instanceof GraphicSelectionListener) {
view2d.getGraphicManager().addGraphicSelectionListener((GraphicSelectionListener) p);
}
}
}
return true;
}
public void updateKeyObjectComponentsListener(ViewCanvas<DicomImageElement> view2d) {
if (view2d != null) {
Optional<ComboItemListener> koSelectionAction = getAction(ActionW.KO_SELECTION, ComboItemListener.class);
Optional<ToggleButtonListener> koToggleAction =
getAction(ActionW.KO_TOOGLE_STATE, ToggleButtonListener.class);
Optional<ToggleButtonListener> koFilterAction = getAction(ActionW.KO_FILTER, ToggleButtonListener.class);
if (JMVUtils.getNULLtoFalse(view2d.getActionValue("no.ko"))) { //$NON-NLS-1$
koToggleAction.ifPresent(a -> a.enableAction(false));
koFilterAction.ifPresent(a -> a.enableAction(false));
koSelectionAction.ifPresent(a -> a.enableAction(false));
} else {
koToggleAction.ifPresent(a -> a
.setSelectedWithoutTriggerAction((Boolean) view2d.getActionValue(ActionW.KO_TOOGLE_STATE.cmd())));
koFilterAction.ifPresent(
a -> a.setSelectedWithoutTriggerAction((Boolean) view2d.getActionValue(ActionW.KO_FILTER.cmd())));
Object[] kos = KOManager.getKOElementListWithNone(view2d).toArray();
boolean enable = kos.length > 1;
if (enable) {
koSelectionAction.ifPresent(a -> a.setDataListWithoutTriggerAction(kos));
koSelectionAction.ifPresent(
a -> a.setSelectedItemWithoutTriggerAction(view2d.getActionValue(ActionW.KO_SELECTION.cmd())));
}
koFilterAction.ifPresent(a -> a.enableAction(enable));
koSelectionAction.ifPresent(a -> a.enableAction(enable));
}
}
}
private void updateWindowLevelComponentsListener(DicomImageElement image, ViewCanvas<DicomImageElement> view2d) {
ImageOpNode node = view2d.getDisplayOpManager().getNode(WindowOp.OP_NAME);
if (node != null) {
int imageDataType = image.getImage().getSampleModel().getDataType();
PresetWindowLevel preset = (PresetWindowLevel) node.getParam(ActionW.PRESET.cmd());
boolean defaultPreset = JMVUtils.getNULLtoTrue(node.getParam(ActionW.DEFAULT_PRESET.cmd()));
Double windowValue = (Double) node.getParam(ActionW.WINDOW.cmd());
Double levelValue = (Double) node.getParam(ActionW.LEVEL.cmd());
LutShape lutShapeItem = (LutShape) node.getParam(ActionW.LUT_SHAPE.cmd());
boolean pixelPadding = JMVUtils.getNULLtoTrue(node.getParam(ActionW.IMAGE_PIX_PADDING.cmd()));
PresentationStateReader prReader =
(PresentationStateReader) view2d.getActionValue(PresentationStateReader.TAG_PR_READER);
getAction(ActionW.DEFAULT_PRESET, ToggleButtonListener.class)
.ifPresent(a -> a.setSelectedWithoutTriggerAction(defaultPreset));
Optional<SliderChangeListener> windowAction = getAction(ActionW.WINDOW, SliderChangeListener.class);
Optional<SliderChangeListener> levelAction = getAction(ActionW.LEVEL, SliderChangeListener.class);
if (windowAction.isPresent() && levelAction.isPresent()) {
double window;
double minLevel;
double maxLevel;
if (windowValue == null) {
windowValue = windowAction.get().getRealValue();
}
if (levelValue == null) {
levelValue = levelAction.get().getRealValue();
}
Double levelMin = (Double) node.getParam(ActionW.LEVEL_MIN.cmd());
Double levelMax = (Double) node.getParam(ActionW.LEVEL_MAX.cmd());
if (levelMin == null || levelMax == null) {
minLevel = Math.min(levelValue - windowValue / 2.0, image.getMinValue(prReader, pixelPadding));
maxLevel = Math.max(levelValue + windowValue / 2.0, image.getMaxValue(prReader, pixelPadding));
} else {
minLevel = Math.min(levelMin, image.getMinValue(prReader, pixelPadding));
maxLevel = Math.max(levelMax, image.getMaxValue(prReader, pixelPadding));
}
window = Math.max(windowValue, maxLevel - minLevel);
windowAction.get().setRealMinMaxValue(imageDataType >= DataBuffer.TYPE_FLOAT ? 0.00001 : 1.0, window,
windowValue, false);
levelAction.get().setRealMinMaxValue(minLevel, maxLevel, levelValue, false);
}
List<PresetWindowLevel> presetList = image.getPresetList(pixelPadding);
if (prReader != null) {
List<PresetWindowLevel> prPresets =
(List<PresetWindowLevel>) view2d.getActionValue(PRManager.PR_PRESETS);
if (prPresets != null && !prPresets.isEmpty()) {
presetList = prPresets;
}
}
Optional<ComboItemListener> presetAction = getAction(ActionW.PRESET, ComboItemListener.class);
if (presetAction.isPresent()) {
presetAction.get().setDataListWithoutTriggerAction(presetList == null ? null : presetList.toArray());
presetAction.get().setSelectedItemWithoutTriggerAction(preset);
}
Optional<ComboItemListener> lutShapeAction = getAction(ActionW.LUT_SHAPE, ComboItemListener.class);
if (lutShapeAction.isPresent()) {
Collection<LutShape> lutShapeList = imageDataType >= DataBuffer.TYPE_INT
? Arrays.asList(LutShape.LINEAR) : image.getLutShapeCollection(pixelPadding);
if (prReader != null && lutShapeList != null && lutShapeItem != null
&& !lutShapeList.contains(lutShapeItem)) {
// Make a copy of the image list
ArrayList<LutShape> newList = new ArrayList<>(lutShapeList.size() + 1);
newList.add(lutShapeItem);
newList.addAll(lutShapeList);
lutShapeList = newList;
}
lutShapeAction.get()
.setDataListWithoutTriggerAction(lutShapeList == null ? null : lutShapeList.toArray());
lutShapeAction.get().setSelectedItemWithoutTriggerAction(lutShapeItem);
}
}
}
@Override
protected boolean isCompatible(MediaSeries<DicomImageElement> series1, MediaSeries<DicomImageElement> series2) {
// Have the two series the same image plane orientation
return ImageOrientation.hasSameOrientation(series1, series2);
}
@Override
public void updateAllListeners(ImageViewerPlugin<DicomImageElement> viewerPlugin, SynchView synchView) {
clearAllPropertyChangeListeners();
if (viewerPlugin != null) {
ViewCanvas<DicomImageElement> viewPane = viewerPlugin.getSelectedImagePane();
if (viewPane == null) {
return;
}
SynchData synch = synchView.getSynchData();
MediaSeries<DicomImageElement> series = viewPane.getSeries();
if (series != null) {
SynchData oldSynch = (SynchData) viewPane.getActionValue(ActionW.SYNCH_LINK.cmd());
if (oldSynch == null || !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch;
}
viewPane.setActionsInView(ActionW.SYNCH_LINK.cmd(), null);
addPropertyChangeListener(ActionW.SYNCH.cmd(), viewPane);
Optional<SliderCineListener> cineAction = getAction(ActionW.SCROLL_SERIES, SliderCineListener.class);
// if (viewPane instanceof MipView) {
// // Handle special case with MIP view, do not let scroll the series
// moveTroughSliceAction.enableAction(false);
// synchView = SynchView.NONE;
// synch = synchView.getSynchData();
// } else {
cineAction.ifPresent(a -> a.enableAction(true));
// }
final List<ViewCanvas<DicomImageElement>> panes = viewerPlugin.getImagePanels();
panes.remove(viewPane);
viewPane.setActionsInView(ActionW.SYNCH_CROSSLINE.cmd(), false);
if (SynchView.NONE.equals(synchView)) {
for (int i = 0; i < panes.size(); i++) {
ViewCanvas<DicomImageElement> pane = panes.get(i);
pane.getGraphicManager().deleteByLayerType(LayerType.CROSSLINES);
MediaSeries<DicomImageElement> s = pane.getSeries();
String fruid = TagD.getTagValue(series, Tag.FrameOfReferenceUID, String.class);
boolean specialView = pane instanceof MipView;
if (s != null && fruid != null && !specialView) {
if (fruid.equals(TagD.getTagValue(s, Tag.FrameOfReferenceUID))) {
if (!ImageOrientation.hasSameOrientation(series, s)) {
pane.setActionsInView(ActionW.SYNCH_CROSSLINE.cmd(), true);
propertySupport.addPropertyChangeListener(ActionW.SCROLL_SERIES.cmd(), pane);
}
// Force to draw crosslines without changing the slice position
cineAction.ifPresent(a -> a.stateChanged(a.getSliderModel()));
}
}
oldSynch = (SynchData) pane.getActionValue(ActionW.SYNCH_LINK.cmd());
if (oldSynch == null || !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch;
}
pane.setActionsInView(ActionW.SYNCH_LINK.cmd(), oldSynch);
// pane.updateSynchState();
}
} else {
// TODO if Pan is activated than rotation is required
if (Mode.STACK.equals(synch.getMode())) {
String fruid = TagD.getTagValue(series, Tag.FrameOfReferenceUID, String.class);
DicomImageElement img = series.getMedia(MEDIA_POSITION.MIDDLE, null, null);
double[] val = img == null ? null : (double[]) img.getTagValue(TagW.SlicePosition);
for (int i = 0; i < panes.size(); i++) {
ViewCanvas<DicomImageElement> pane = panes.get(i);
pane.getGraphicManager().deleteByLayerType(LayerType.CROSSLINES);
MediaSeries<DicomImageElement> s = pane.getSeries();
boolean specialView = pane instanceof MipView;
if (s != null && fruid != null && val != null && !specialView) {
boolean synchByDefault = fruid.equals(TagD.getTagValue(s, Tag.FrameOfReferenceUID));
oldSynch = (SynchData) pane.getActionValue(ActionW.SYNCH_LINK.cmd());
if (synchByDefault) {
if (ImageOrientation.hasSameOrientation(series, s)) {
pane.setActionsInView(ActionW.SYNCH_CROSSLINE.cmd(), false);
// Only fully synch if no PR is applied (because can change pixel size)
if (pane.getActionValue(PresentationStateReader.TAG_PR_READER) == null
&& hasSameSize(series, s)) {
// If the image has the same reference and the same spatial calibration, all
// the actions are synchronized
if (oldSynch == null || oldSynch.isOriginal()
|| !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch.copy();
}
} else {
if (oldSynch == null || oldSynch.isOriginal()
|| !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch.copy();
for (Entry<String, Boolean> a : oldSynch.getActions().entrySet()) {
a.setValue(false);
}
oldSynch.getActions().put(ActionW.SCROLL_SERIES.cmd(), true);
}
}
} else {
pane.setActionsInView(ActionW.SYNCH_CROSSLINE.cmd(), true);
if (pane instanceof MprView) {
if (oldSynch == null || oldSynch.isOriginal()
|| !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch.copy();
}
} else {
if (oldSynch == null || oldSynch.isOriginal()
|| !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch.copy();
for (Entry<String, Boolean> a : oldSynch.getActions().entrySet()) {
a.setValue(false);
}
oldSynch.getActions().put(ActionW.SCROLL_SERIES.cmd(), true);
}
}
}
addPropertyChangeListener(ActionW.SYNCH.cmd(), pane);
}
pane.setActionsInView(ActionW.SYNCH_LINK.cmd(), oldSynch);
// pane.updateSynchState();
}
}
// Force to draw crosslines without changing the slice position
cineAction.ifPresent(a -> a.stateChanged(a.getSliderModel()));
} else if (Mode.TILE.equals(synch.getMode())) {
// Limit the scroll
final int maxShift = series.size((Filter<DicomImageElement>) viewPane.getActionValue(ActionW.FILTERED_SERIES.cmd())) - panes.size();
cineAction.ifPresent(a -> a.setSliderMinMaxValue(1, maxShift < 1 ? 1 : maxShift, viewPane.getFrameIndex() + 1, false));
Object selectedKO = viewPane.getActionValue(ActionW.KO_SELECTION.cmd());
Boolean enableFilter = (Boolean) viewPane.getActionValue(ActionW.KO_FILTER.cmd());
int frameIndex = JMVUtils.getNULLtoFalse(enableFilter) ? 0
: viewPane.getFrameIndex() - viewPane.getTileOffset();
for (int i = 0; i < panes.size(); i++) {
ViewCanvas<DicomImageElement> pane = panes.get(i);
oldSynch = (SynchData) pane.getActionValue(ActionW.SYNCH_LINK.cmd());
if (oldSynch == null || !oldSynch.getMode().equals(synch.getMode())) {
oldSynch = synch.copy();
}
oldSynch.getActions().put(ActionW.KO_SELECTION.cmd(), true);
oldSynch.getActions().put(ActionW.KO_FILTER.cmd(), true);
KOManager.updateKOFilter(pane, selectedKO, enableFilter, frameIndex);
pane.setActionsInView(ActionW.SYNCH_LINK.cmd(), oldSynch);
pane.setActionsInView(ActionW.SYNCH_CROSSLINE.cmd(), false);
addPropertyChangeListener(ActionW.SYNCH.cmd(), pane);
// pane.updateSynchState();
}
if (JMVUtils.getNULLtoFalse(enableFilter)) {
KOManager.updateKOFilter(viewPane, selectedKO, enableFilter, frameIndex);
}
}
}
}
// viewPane.updateSynchState();
}
}
public static boolean hasSameSize(MediaSeries<DicomImageElement> series1, MediaSeries<DicomImageElement> series2) {
// Test if the two series has the same size
if (series1 != null && series2 != null) {
DicomImageElement image1 = series1.getMedia(MEDIA_POSITION.MIDDLE, null, null);
DicomImageElement image2 = series2.getMedia(MEDIA_POSITION.MIDDLE, null, null);
if (image1 != null && image2 != null) {
return image1.hasSameSize(image2);
}
}
return false;
}
public void savePreferences(BundleContext bundleContext) {
Preferences prefs = BundlePreferences.getDefaultPreferences(bundleContext);
zoomSetting.savePreferences(prefs);
// Mouse buttons preferences
mouseActions.savePreferences(prefs);
if (prefs != null) {
// Mouse sensitivity
Preferences prefNode = prefs.node("mouse.sensivity"); //$NON-NLS-1$
setSliderPreference(prefNode, ActionW.WINDOW);
setSliderPreference(prefNode, ActionW.LEVEL);
setSliderPreference(prefNode, ActionW.SCROLL_SERIES);
setSliderPreference(prefNode, ActionW.ROTATION);
setSliderPreference(prefNode, ActionW.ZOOM);
prefNode = prefs.node("other"); //$NON-NLS-1$
BundlePreferences.putBooleanPreferences(prefNode, WindowOp.P_APPLY_WL_COLOR,
options.getBooleanProperty(WindowOp.P_APPLY_WL_COLOR, true));
Preferences containerNode = prefs.node(View2dContainer.class.getSimpleName().toLowerCase());
InsertableUtil.savePreferences(View2dContainer.TOOLBARS, containerNode, Type.TOOLBAR);
InsertableUtil.savePreferences(View2dContainer.TOOLS, containerNode, Type.TOOL);
InsertableUtil.savePreferences(MPRContainer.TOOLBARS,
prefs.node(MPRContainer.class.getSimpleName().toLowerCase()), Type.TOOLBAR);
}
}
private void setSliderPreference(Preferences prefNode, ActionW action) {
Optional<SliderChangeListener> sliderAction = getAction(action, SliderChangeListener.class);
if (sliderAction.isPresent()) {
BundlePreferences.putDoublePreferences(prefNode, action.cmd(), sliderAction.get().getMouseSensivity());
}
}
private void getSliderPreference(Preferences prefNode, ActionW action, double defVal) {
Optional<SliderChangeListener> sliderAction = getAction(action, SliderChangeListener.class);
if (sliderAction.isPresent()) {
sliderAction.get().setMouseSensivity(prefNode.getDouble(action.cmd(), defVal));
}
}
public MediaSeries<DicomImageElement> getSelectedSeries() {
ViewCanvas<DicomImageElement> pane = getSelectedViewPane();
if (pane != null) {
return pane.getSeries();
}
return null;
}
public JMenu getResetMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
ButtonGroup group = new ButtonGroup();
menu = new JMenu(Messages.getString("ResetTools.reset")); //$NON-NLS-1$
menu.setIcon(new ImageIcon(DefaultView2d.class.getResource("/icon/16x16/reset.png"))); //$NON-NLS-1$
menu.setEnabled(getSelectedSeries() != null);
if (menu.isEnabled()) {
for (final ResetTools action : ResetTools.values()) {
final JMenuItem item = new JMenuItem(action.toString());
item.addActionListener(e -> reset(action));
menu.add(item);
group.add(item);
}
}
}
return menu;
}
public JMenu getPresetMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ComboItemListener> presetAction = getAction(ActionW.PRESET, ComboItemListener.class);
if (presetAction.isPresent()) {
menu = presetAction.get().createUnregisteredRadioMenu(Messages.getString("View2dContainer.presets"));//$NON-NLS-1$
menu.setIcon(ActionW.WINLEVEL.getSmallIcon());
for (Component mitem : menu.getMenuComponents()) {
RadioMenuItem ritem = (RadioMenuItem) mitem;
PresetWindowLevel preset = (PresetWindowLevel) ritem.getUserObject();
if (preset.getKeyCode() > 0) {
ritem.setAccelerator(KeyStroke.getKeyStroke(preset.getKeyCode(), 0));
}
}
}
}
return menu;
}
public JMenu getLutShapeMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ComboItemListener> lutShapeAction = getAction(ActionW.LUT_SHAPE, ComboItemListener.class);
if (lutShapeAction.isPresent()) {
menu = lutShapeAction.get().createUnregisteredRadioMenu(ActionW.LUT_SHAPE.getTitle());
}
}
return menu;
}
public JMenu getZoomMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<SliderChangeListener> zoomAction = getAction(ActionW.ZOOM, SliderChangeListener.class);
if (zoomAction.isPresent()) {
menu = new JMenu(ActionW.ZOOM.getTitle());
menu.setIcon(ActionW.ZOOM.getSmallIcon());
menu.setEnabled(zoomAction.get().isActionEnabled());
if (zoomAction.get().isActionEnabled()) {
for (JMenuItem jMenuItem : ZoomToolBar.getZoomListMenuItems(this)) {
menu.add(jMenuItem);
}
}
}
}
return menu;
}
public JMenu getOrientationMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<SliderChangeListener> rotateAction = getAction(ActionW.ROTATION, SliderChangeListener.class);
if (rotateAction.isPresent()) {
menu = new JMenu(Messages.getString("View2dContainer.orientation")); //$NON-NLS-1$
menu.setIcon(ActionW.ROTATION.getSmallIcon());
menu.setEnabled(rotateAction.get().isActionEnabled());
if (rotateAction.get().isActionEnabled()) {
JMenuItem menuItem = new JMenuItem(Messages.getString("ResetTools.reset")); //$NON-NLS-1$
menuItem.addActionListener(e -> rotateAction.get().setSliderValue(0));
menu.add(menuItem);
menuItem = new JMenuItem(Messages.getString("View2dContainer.-90")); //$NON-NLS-1$
menuItem.addActionListener(
e -> rotateAction.get().setSliderValue((rotateAction.get().getSliderValue() - 90 + 360) % 360));
menu.add(menuItem);
menuItem = new JMenuItem(Messages.getString("View2dContainer.+90")); //$NON-NLS-1$
menuItem.addActionListener(
e -> rotateAction.get().setSliderValue((rotateAction.get().getSliderValue() + 90) % 360));
menu.add(menuItem);
menuItem = new JMenuItem(Messages.getString("View2dContainer.+180")); //$NON-NLS-1$
menuItem.addActionListener(
e -> rotateAction.get().setSliderValue((rotateAction.get().getSliderValue() + 180) % 360));
menu.add(menuItem);
Optional<ToggleButtonListener> flipAction = getAction(ActionW.FLIP, ToggleButtonListener.class);
if (flipAction.isPresent()) {
menu.add(new JSeparator());
menu.add(flipAction.get()
.createUnregiteredJCheckBoxMenuItem(Messages.getString("View2dContainer.flip_h"))); //$NON-NLS-1$
}
}
}
}
return menu;
}
public JMenu getSortStackMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ComboItemListener> sortStackAction = getAction(ActionW.SORTSTACK, ComboItemListener.class);
if (sortStackAction.isPresent()) {
menu =
sortStackAction.get().createUnregisteredRadioMenu(Messages.getString("View2dContainer.sort_stack")); //$NON-NLS-1$
Optional<ToggleButtonListener> inverseStackAction =
getAction(ActionW.INVERSESTACK, ToggleButtonListener.class);
if (inverseStackAction.isPresent()) {
menu.add(new JSeparator());
menu.add(inverseStackAction.get()
.createUnregiteredJCheckBoxMenuItem(Messages.getString("View2dContainer.inv_stack"))); //$NON-NLS-1$
}
}
}
return menu;
}
public JMenu getLutMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ComboItemListener> lutAction = getAction(ActionW.LUT, ComboItemListener.class);
if (lutAction.isPresent()) {
menu = lutAction.get().createUnregisteredRadioMenu(Messages.getString("ImageTool.lut"));//$NON-NLS-1$
}
}
return menu;
}
public JCheckBoxMenuItem getLutInverseMenu(String prop) {
JCheckBoxMenuItem menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ToggleButtonListener> inverseLutAction = getAction(ActionW.INVERT_LUT, ToggleButtonListener.class);
if (inverseLutAction.isPresent()) {
menu = inverseLutAction.get().createUnregiteredJCheckBoxMenuItem(ActionW.INVERT_LUT.getTitle());
}
}
return menu;
}
public JMenu getFilterMenu(String prop) {
JMenu menu = null;
if (BundleTools.SYSTEM_PREFERENCES.getBooleanProperty(prop, true)) {
Optional<ComboItemListener> filterAction = getAction(ActionW.FILTER, ComboItemListener.class);
if (filterAction.isPresent()) {
menu = filterAction.get().createUnregisteredRadioMenu(Messages.getString("ImageTool.filter")); //$NON-NLS-1$
}
}
return menu;
}
// ***** OSGI commands: dcmview2d:cmd ***** //
public void zoom(String[] argv) throws IOException {
final String[] usage = { "Change the zoom value of the selected image", //$NON-NLS-1$
"Usage: dcmview2d:zoom (set VALUE | increase NUMBER | decrease NUMBER)", //$NON-NLS-1$
" -s --set=VALUE [decimal value] set a new value from 0.0 to 12.0 (zoom magnitude, 0.0 => default, -200.0 => best fit, -100.0 => real size)", //$NON-NLS-1$
" -i --increase=NUMBER increase of some amount", //$NON-NLS-1$
" -d --decrease=NUMBER decrease of some amount", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || !opt.isOnlyOneOptionActivate("set", "increase", "decrease")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> zoomCommand(opt, args));
}
private void zoomCommand(Option opt, List<String> args) {
try {
Optional<SliderChangeListener> zoomAction = getAction(ActionW.ZOOM, SliderChangeListener.class);
if (zoomAction.isPresent()) {
if (opt.isSet("increase")) { //$NON-NLS-1$
zoomAction.get().setSliderValue(zoomAction.get().getSliderValue() + opt.getNumber("increase")); //$NON-NLS-1$
} else if (opt.isSet("decrease")) { //$NON-NLS-1$
zoomAction.get().setSliderValue(zoomAction.get().getSliderValue() - opt.getNumber("decrease")); //$NON-NLS-1$
} else if (opt.isSet("set")) { //$NON-NLS-1$
double val3 = Double.parseDouble(opt.get("set")); //$NON-NLS-1$
if (val3 <= 0.0) {
firePropertyChange(ActionW.SYNCH.cmd(), null,
new SynchEvent(getSelectedViewPane(), ActionW.ZOOM.cmd(), val3));
} else {
zoomAction.get().setRealValue(val3);
}
}
}
} catch (Exception e) {
LOGGER.error("Zoom command: {}", args.get(0), e); //$NON-NLS-1$
}
}
public void wl(String[] argv) throws IOException {
final String[] usage = {
"Change the window/level values of the selected image (increase or decrease into a normalized range of 4096)", //$NON-NLS-1$
"Usage: dcmview2d:wl -- WIN LEVEL", //$NON-NLS-1$
"WIN and LEVEL are Integer. It is mandatory to have '--' (end of options) for negative values", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || args.size() != 2) { //$NON-NLS-1$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
try {
Optional<SliderChangeListener> windowAction = getAction(ActionW.WINDOW, SliderChangeListener.class);
Optional<SliderChangeListener> levelAction = getAction(ActionW.LEVEL, SliderChangeListener.class);
if (windowAction.isPresent() && levelAction.isPresent()) {
int win = windowAction.get().getSliderValue() + Integer.parseInt(args.get(0));
int level = levelAction.get().getSliderValue() + Integer.parseInt(args.get(1));
windowAction.get().setSliderValue(win);
levelAction.get().setSliderValue(level);
}
} catch (Exception e) {
LOGGER.error("Window/level command: {} {}", args.get(0), args.get(1), e); //$NON-NLS-1$
}
});
}
public void move(String[] argv) throws IOException {
final String[] usage = { "Pan the selected image", //$NON-NLS-1$
"Usage: dcmview2d:move -- X Y", //$NON-NLS-1$
"X and Y are Integer. It is mandatory to have '--' (end of options) for negative values", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || args.size() != 2) { //$NON-NLS-1$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
try {
int valx = Integer.parseInt(args.get(0));
int valy = Integer.parseInt(args.get(1));
getAction(ActionW.PAN, PannerListener.class)
.ifPresent(a -> a.setPoint(new PanPoint(PanPoint.State.MOVE, valx, valy)));
} catch (Exception e) {
LOGGER.error("Move (x,y) command: {} {}", args.get(0), args.get(1), e); //$NON-NLS-1$
}
});
}
public void scroll(String[] argv) throws IOException {
final String[] usage = { "Scroll into the images of the selected series", //$NON-NLS-1$
"Usage: dcmview2d:scroll ( -s NUMBER | -i NUMBER | -d NUMBER)", //$NON-NLS-1$
" -s --set=NUMBER set a new value from 1 to series size", //$NON-NLS-1$
" -i --increase=NUMBER increase of some amount", //$NON-NLS-1$
" -d --decrease=NUMBER decrease of some amount", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
if (opt.isSet("help") || !opt.isOnlyOneOptionActivate("set", "increase", "decrease")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
try {
Optional<SliderCineListener> cineAction = getAction(ActionW.SCROLL_SERIES, SliderCineListener.class);
if (cineAction.isPresent() && cineAction.get().isActionEnabled()) {
SliderCineListener moveTroughSliceAction = cineAction.get();
if (opt.isSet("increase")) { //$NON-NLS-1$
moveTroughSliceAction
.setSliderValue(moveTroughSliceAction.getSliderValue() + opt.getNumber("increase")); //$NON-NLS-1$
} else if (opt.isSet("decrease")) { //$NON-NLS-1$
moveTroughSliceAction
.setSliderValue(moveTroughSliceAction.getSliderValue() - opt.getNumber("decrease")); //$NON-NLS-1$
} else if (opt.isSet("set")) { //$NON-NLS-1$
moveTroughSliceAction.setSliderValue(opt.getNumber("set")); //$NON-NLS-1$
}
}
} catch (Exception e) {
LOGGER.error("Scroll command error:", e); //$NON-NLS-1$
}
});
}
public void layout(String[] argv) throws IOException {
final String[] usage = { "Select a split-screen layout", //$NON-NLS-1$
"Usage: dcmview2d:layout ( -n NUMBER | -i ID )", //$NON-NLS-1$
" -n --number=NUMBER select the best matching number of views", //$NON-NLS-1$
" -i --id=ID select the layout from its identifier", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
if (opt.isSet("help") || !opt.isOnlyOneOptionActivate("number", "id")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
try {
if (opt.isSet("number")) { //$NON-NLS-1$
if (selectedView2dContainer != null) {
GridBagLayoutModel val1 =
selectedView2dContainer.getBestDefaultViewLayout(opt.getNumber("number")); //$NON-NLS-1$
getAction(ActionW.LAYOUT, ComboItemListener.class).ifPresent(a -> a.setSelectedItem(val1));
}
} else if (opt.isSet("id")) { //$NON-NLS-1$
if (selectedView2dContainer != null) {
GridBagLayoutModel val2 = selectedView2dContainer.getViewLayout(opt.get("id")); //$NON-NLS-1$
if (val2 != null) {
getAction(ActionW.LAYOUT, ComboItemListener.class).ifPresent(a -> a.setSelectedItem(val2));
}
}
}
} catch (Exception e) {
LOGGER.error("Layout command error", e); //$NON-NLS-1$
}
});
}
public void mouseLeftAction(String[] argv) throws IOException {
final String[] usage = { "Change the mouse left action", //$NON-NLS-1$
"Usage: dcmview2d:mouseLeftAction COMMAND", //$NON-NLS-1$
"COMMAND is (sequence|winLevel|zoom|pan|rotation|crosshair|measure|draw|contextMenu|none)", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || args.size() != 1) { //$NON-NLS-1$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
String command = args.get(0);
if (command != null) {
try {
if (command.startsWith("session")) { //$NON-NLS-1$
AuditLog.LOGGER.info("source:telnet {}", command); //$NON-NLS-1$
} else {
AuditLog.LOGGER.info("source:telnet mouse:{} action:{}", MouseActions.LEFT, command); //$NON-NLS-1$
excecuteMouseAction(command);
}
} catch (Exception e) {
LOGGER.error("Mouse command: {}", command, e); //$NON-NLS-1$
}
}
});
}
private void excecuteMouseAction(String command) {
if (!command.equals(mouseActions.getAction(MouseActions.LEFT))) {
mouseActions.setAction(MouseActions.LEFT, command);
ImageViewerPlugin<DicomImageElement> view = getSelectedView2dContainer();
if (view != null) {
view.setMouseActions(mouseActions);
final ViewerToolBar toolBar = view.getViewerToolBar();
if (toolBar != null) {
// Test if mouse action exist and if not NO_ACTION is set
ActionW action = toolBar.getAction(ViewerToolBar.actionsButtons, command);
if (action == null) {
command = ActionW.NO_ACTION.cmd();
}
toolBar.changeButtonState(MouseActions.LEFT, command);
}
}
}
}
public void synch(String[] argv) throws IOException {
final String[] usage = { "Set a synchronization mode", //$NON-NLS-1$
"Usage: dcmview2d:synch VALUE", //$NON-NLS-1$
"VALUE is " + View2dContainer.SYNCH_LIST.stream().map(s -> s.getCommand()) //$NON-NLS-1$
.collect(Collectors.joining("|", "(", ")")), //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || args.size() != 1) { //$NON-NLS-1$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
String command = args.get(0);
if (command != null) {
ImageViewerPlugin<DicomImageElement> view = getSelectedView2dContainer();
if (view != null) {
try {
Optional<ComboItemListener> synchAction = getAction(ActionW.SYNCH, ComboItemListener.class);
if (synchAction.isPresent()) {
for (SynchView synch : view.getSynchList()) {
if (synch.getCommand().equals(command)) {
synchAction.get().setSelectedItem(synch);
return;
}
}
throw new IllegalArgumentException(command + " not found!"); //$NON-NLS-1$
}
} catch (Exception e) {
LOGGER.error("Synch command: {}", command, e); //$NON-NLS-1$
}
}
}
});
}
public void reset(String[] argv) throws IOException {
final String[] usage = { "Reset image display", //$NON-NLS-1$
"Usage: dcmview2d:reset (-a | COMMAND...)", //$NON-NLS-1$
"COMMAND is (winLevel|zoom|pan|rotation)", //$NON-NLS-1$
" -a --all reset to original display", //$NON-NLS-1$
" -? --help show help" }; //$NON-NLS-1$
final Option opt = Options.compile(usage).parse(argv);
final List<String> args = opt.args();
if (opt.isSet("help") || args.isEmpty() && !opt.isSet("all")) { //$NON-NLS-1$ //$NON-NLS-2$
opt.usage();
return;
}
GuiExecutor.instance().execute(() -> {
if (opt.isSet("all")) { //$NON-NLS-1$
reset(ResetTools.All);
} else {
for (String command : args) {
try {
if (ActionW.WINLEVEL.cmd().equals(command)) {
reset(ResetTools.WindowLevel);
} else if (ActionW.ZOOM.cmd().equals(command)) {
reset(ResetTools.Zoom);
} else if (ActionW.PAN.cmd().equals(command)) {
reset(ResetTools.Pan);
} else if (ActionW.ROTATION.cmd().equals(command)) {
reset(ResetTools.Rotation);
} else {
LOGGER.warn("Reset command not found: {}", command); //$NON-NLS-1$
}
} catch (Exception e) {
LOGGER.error("Reset command: {}", command, e); //$NON-NLS-1$
}
}
}
});
}
}