/******************************************************************************* * 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.util.ArrayList; import java.util.Collection; import java.util.List; import javax.swing.JOptionPane; import org.dcm4che3.data.Attributes; import org.dcm4che3.data.Tag; import org.weasis.core.api.explorer.ObservableEvent; import org.weasis.core.api.gui.util.ActionState; import org.weasis.core.api.gui.util.ActionW; import org.weasis.core.api.gui.util.ComboItemListener; 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.gui.util.ToggleButtonListener; import org.weasis.core.api.media.data.MediaElement; import org.weasis.core.api.media.data.MediaSeries; import org.weasis.core.api.media.data.SeriesEvent; import org.weasis.core.api.media.data.TagW; import org.weasis.core.api.util.StringUtil; import org.weasis.core.ui.editor.image.ViewCanvas; import org.weasis.dicom.codec.DcmMediaReader; import org.weasis.dicom.codec.DicomImageElement; import org.weasis.dicom.codec.DicomSeries; import org.weasis.dicom.codec.KOSpecialElement; import org.weasis.dicom.codec.TagD; import org.weasis.dicom.codec.macro.HierachicalSOPInstanceReference; import org.weasis.dicom.codec.macro.KODocumentModule; import org.weasis.dicom.codec.utils.DicomMediaUtils; import org.weasis.dicom.explorer.DicomModel; import org.weasis.dicom.explorer.LoadDicomObjects; public final class KOManager { public static List<Object> getKOElementListWithNone(ViewCanvas<DicomImageElement> currentView) { Collection<KOSpecialElement> koElements = currentView != null ? DicomModel.getKoSpecialElements(currentView.getSeries()) : null; int koElementNb = (koElements == null) ? 0 : koElements.size(); List<Object> koElementListWithNone = new ArrayList<>(koElementNb + 1); koElementListWithNone.add(ActionState.NoneLabel.NONE); if (koElementNb > 0) { koElementListWithNone.addAll(koElements); } return koElementListWithNone; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Test if current sopInstanceUID is referenced in the selected KEY_OBJECT of the given currentView. If not, search * if there is a more suitable new KEY_OBJECT element. Ask the user if needed. */ public static KOSpecialElement getValidKOSelection(final ViewCanvas<DicomImageElement> view2d) { KOSpecialElement currentSelectedKO = getCurrentKOSelection(view2d); DicomImageElement currentImage = view2d.getImage(); KOSpecialElement newKOSelection = null; Attributes newDicomKO = null; if (currentSelectedKO == null) { KOSpecialElement validKOSelection = findValidKOSelection(view2d); if (validKOSelection != null) { String message = Messages.getString("KOManager.select_KO_msg"); //$NON-NLS-1$ Object[] options = { Messages.getString("KOManager.select_last_ko"), Messages.getString("KOManager.new_ko") }; //$NON-NLS-1$ //$NON-NLS-2$ int response = JOptionPane.showOptionDialog(view2d.getJComponent(), message, Messages.getString("KOManager.ko_title"), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (response == 0) { newKOSelection = validKOSelection; } else if (response == 1) { newDicomKO = createNewDicomKeyObject(currentImage, view2d.getJComponent()); } else if (response == JOptionPane.CLOSED_OPTION) { return null; } } else { newDicomKO = createNewDicomKeyObject(currentImage, view2d.getJComponent()); } } else { if (currentSelectedKO.getMediaReader().isEditableDicom()) { String studyInstanceUID = TagD.getTagValue(currentImage, Tag.StudyInstanceUID, String.class); if (currentSelectedKO.isEmpty() || currentSelectedKO.containsStudyInstanceUIDReference(studyInstanceUID)) { newKOSelection = currentSelectedKO; } else { String message = Messages.getString("KOManager.no_ko_msg"); //$NON-NLS-1$ Object[] options = { Messages.getString("KOManager.use_ko"), Messages.getString("KOManager.new_ko") }; //$NON-NLS-1$ //$NON-NLS-2$ int response = JOptionPane.showOptionDialog(view2d.getJComponent(), message, Messages.getString("KOManager.ko_title"), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (response == 0) { newKOSelection = currentSelectedKO; } else if (response == 1) { newDicomKO = createNewDicomKeyObject(currentImage, view2d.getJComponent()); } else if (response == JOptionPane.CLOSED_OPTION) { return null; } } } else { String message = Messages.getString("KOManager.ko_readonly"); //$NON-NLS-1$ Object[] options = { Messages.getString("KOManager.new_ko"), Messages.getString("KOManager.new_ko_from") }; //$NON-NLS-1$ //$NON-NLS-2$ int response = JOptionPane.showOptionDialog(view2d.getJComponent(), message, Messages.getString("KOManager.ko_title"), //$NON-NLS-1$ JOptionPane.YES_NO_OPTION, JOptionPane.WARNING_MESSAGE, null, options, options[0]); if (response == 0) { newDicomKO = createNewDicomKeyObject(currentImage, view2d.getJComponent()); } else if (response == 1) { newDicomKO = createNewDicomKeyObject(currentSelectedKO, view2d.getJComponent()); } else if (response == JOptionPane.CLOSED_OPTION) { return null; } } } if (newDicomKO != null) { // Deactivate filter for new KO ActionState koFilterAction = view2d.getEventManager().getAction(ActionW.KO_FILTER); if (koFilterAction instanceof ToggleButtonListener) { ((ToggleButtonListener) koFilterAction).setSelected(false); } newKOSelection = loadDicomKeyObject(view2d.getSeries(), newDicomKO); } return newKOSelection; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static KOSpecialElement loadDicomKeyObject(MediaSeries<DicomImageElement> dicomSeries, Attributes newDicomKO) { DicomModel dicomModel = (DicomModel) dicomSeries.getTagValue(TagW.ExplorerModel); new LoadDicomObjects(dicomModel, newDicomKO).addSelectionAndnotify(); // must be executed in the EDT for (KOSpecialElement koElement : DicomModel.getKoSpecialElements(dicomSeries)) { if (koElement.getMediaReader().getDicomObject().equals(newDicomKO)) { return koElement; } } return null; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static Attributes createNewDicomKeyObject(MediaElement dicomMediaElement, Component parentComponent) { if (dicomMediaElement != null && dicomMediaElement.getMediaReader() instanceof DcmMediaReader) { Attributes dicomSourceAttribute = ((DcmMediaReader) dicomMediaElement.getMediaReader()).getDicomObject(); String message = Messages.getString("KOManager.ko_desc"); //$NON-NLS-1$ String defautDescription = Messages.getString("KOManager.ko_name"); //$NON-NLS-1$ String description = (String) JOptionPane.showInputDialog(parentComponent, message, Messages.getString("KOManager.ko_title"), //$NON-NLS-1$ JOptionPane.INFORMATION_MESSAGE, null, null, defautDescription); // description==null means the user canceled the input if (StringUtil.hasText(description)) { Attributes ko = DicomMediaUtils.createDicomKeyObject(dicomSourceAttribute, description, null); if (dicomMediaElement instanceof KOSpecialElement) { Collection<HierachicalSOPInstanceReference> referencedStudySequence = new KODocumentModule(dicomSourceAttribute).getCurrentRequestedProcedureEvidences(); new KODocumentModule(ko).setCurrentRequestedProcedureEvidences(referencedStudySequence); } return ko; } } return null; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /** * Get an editable Dicom KeyObject Selection suitable to handle current Dicom Image. A valid object should either * reference the studyInstanceUID of the current Dicom Image or simply be empty ... */ public static KOSpecialElement findValidKOSelection(final ViewCanvas<DicomImageElement> view2d) { MediaSeries<DicomImageElement> dicomSeries = view2d.getSeries(); DicomImageElement currentImage = view2d.getImage(); if (currentImage != null && dicomSeries != null) { String currentStudyInstanceUID = TagD.getTagValue(currentImage, Tag.StudyInstanceUID, String.class); Collection<KOSpecialElement> koElementsWithReferencedSeriesInstanceUID = DicomModel.getKoSpecialElements(dicomSeries); if (koElementsWithReferencedSeriesInstanceUID != null) { for (KOSpecialElement koElement : koElementsWithReferencedSeriesInstanceUID) { if (koElement.getMediaReader().isEditableDicom()) { if (koElement.containsStudyInstanceUIDReference(currentStudyInstanceUID)) { return koElement; } } } for (KOSpecialElement koElement : koElementsWithReferencedSeriesInstanceUID) { if (koElement.getMediaReader().isEditableDicom()) { if (koElement.isEmpty()) { return koElement; } } } } } return null; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static KOSpecialElement getCurrentKOSelection(final ViewCanvas<DicomImageElement> view2d) { Object actionValue = view2d.getActionValue(ActionW.KO_SELECTION.cmd()); if (actionValue instanceof KOSpecialElement) { return (KOSpecialElement) actionValue; } return null; } public static boolean setKeyObjectReference(boolean selectedState, final ViewCanvas<DicomImageElement> view2d) { KOSpecialElement validKOSelection = getValidKOSelection(view2d); if (validKOSelection == null) { return false; // canceled } KOSpecialElement currentSelectedKO = KOManager.getCurrentKOSelection(view2d); if (validKOSelection != currentSelectedKO) { ActionState koSelection = view2d.getEventManager().getAction(ActionW.KO_SELECTION); if (koSelection instanceof ComboItemListener) { ((ComboItemListener) koSelection).setSelectedItem(validKOSelection); } } boolean hasKeyObjectReferenceChanged = false; if (validKOSelection == currentSelectedKO || currentSelectedKO == null) { // KO Toogle State is changed only if KO Selection remains the same, // or if there was no previous KO Selection DicomImageElement currentImage = view2d.getImage(); hasKeyObjectReferenceChanged = validKOSelection.setKeyObjectReference(selectedState, currentImage); if (hasKeyObjectReferenceChanged) { DicomModel dicomModel = (DicomModel) view2d.getSeries().getTagValue(TagW.ExplorerModel); // Fire an event since any view in any View2dContainer may have its KO selected state changed if (dicomModel != null) { dicomModel.firePropertyChange( new ObservableEvent(ObservableEvent.BasicAction.UPDATE, view2d, null, validKOSelection)); } boolean filter = JMVUtils.getNULLtoFalse(view2d.getActionValue(ActionW.KO_FILTER.cmd())); if (filter && (view2d.getEventManager().getSelectedViewPane() == view2d)) { // When unchecking an image, force to call the filter action to resize the views ActionState koFilterAction = view2d.getEventManager().getAction(ActionW.KO_FILTER); if (koFilterAction instanceof ToggleButtonListener) { ((ToggleButtonListener) koFilterAction).setSelectedWithoutTriggerAction(false); ((ToggleButtonListener) koFilterAction).setSelected(true); } } } } return hasKeyObjectReferenceChanged; } public static boolean setKeyObjectReferenceAllSeries(boolean selectedState, final ViewCanvas<DicomImageElement> view2d) { KOSpecialElement validKOSelection = getValidKOSelection(view2d); if (validKOSelection == null) { return false; // canceled } KOSpecialElement currentSelectedKO = KOManager.getCurrentKOSelection(view2d); if (validKOSelection != currentSelectedKO) { ActionState koSelection = view2d.getEventManager().getAction(ActionW.KO_SELECTION); if (koSelection instanceof ComboItemListener) { ((ComboItemListener) koSelection).setSelectedItem(validKOSelection); } } boolean hasKeyObjectReferenceChanged = false; if (validKOSelection == currentSelectedKO || currentSelectedKO == null) { // KO Toogle State is changed only if KO Selection remains the same, // or if there was no previous KO Selection List<DicomImageElement> dicomImageList = view2d.getSeries().getSortedMedias(null); hasKeyObjectReferenceChanged = validKOSelection.setKeyObjectReference(selectedState, dicomImageList); if (hasKeyObjectReferenceChanged) { DicomModel dicomModel = (DicomModel) view2d.getSeries().getTagValue(TagW.ExplorerModel); // Fire an event since any view in any View2dContainer may have its KO selected state changed if (dicomModel != null) { dicomModel.firePropertyChange(new ObservableEvent(ObservableEvent.BasicAction.UPDATE, view2d, null, new SeriesEvent(SeriesEvent.Action.UPDATE, validKOSelection, "updateAll"))); //$NON-NLS-1$ } } } return hasKeyObjectReferenceChanged; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// public static void updateKOFilter(ViewCanvas<DicomImageElement> view2D, Object newSelectedKO, Boolean enableFilter, int imgSelectionIndex) { updateKOFilter(view2D, newSelectedKO, enableFilter, imgSelectionIndex, true); } public static void updateKOFilter(ViewCanvas<DicomImageElement> view2D, Object newSelectedKO, Boolean enableFilter, int imgSelectionIndex, boolean updateImage) { if (view2D instanceof View2d) { boolean tiledMode = imgSelectionIndex >= 0; boolean koFilter = false; KOSpecialElement selectedKO = null; if (newSelectedKO == null) { Object actionValue = view2D.getActionValue(ActionW.KO_SELECTION.cmd()); if (actionValue instanceof KOSpecialElement) { selectedKO = (KOSpecialElement) actionValue; // test if current ko_selection action in view do still exist Collection<KOSpecialElement> koElements = (view2D != null && view2D.getSeries() != null) ? DicomModel.getKoSpecialElements(view2D.getSeries()) : null; if (koElements != null && koElements.contains(selectedKO) == false) { selectedKO = null; newSelectedKO = ActionState.NoneLabel.NONE; view2D.setActionsInView(ActionW.KO_SELECTION.cmd(), newSelectedKO); } } } else { if (newSelectedKO instanceof KOSpecialElement) { selectedKO = (KOSpecialElement) newSelectedKO; } view2D.setActionsInView(ActionW.KO_SELECTION.cmd(), newSelectedKO); } if (enableFilter == null) { koFilter = JMVUtils.getNULLtoFalse(view2D.getActionValue(ActionW.KO_FILTER.cmd())); } else { koFilter = enableFilter; } if (tiledMode && selectedKO == null) { // Unselect the filter with the None KO selection koFilter = false; } view2D.setActionsInView(ActionW.KO_FILTER.cmd(), koFilter); view2D.setActionsInView(ActionW.FILTERED_SERIES.cmd(), null); if (selectedKO == null || view2D.getSeries() == null || (view2D.getImage() == null && !tiledMode)) { if (newSelectedKO != null) { if (updateImage) { updateImage(view2D, null, view2D.getFrameIndex()); } // Update the None KO selection ((View2d) view2D).updateKOButtonVisibleState(); } return; } DicomSeries dicomSeries = (DicomSeries) view2D.getSeries(); String seriesInstanceUID = TagD.getTagValue(dicomSeries, Tag.SeriesInstanceUID, String.class); Filter<DicomImageElement> sopInstanceUIDFilter = null; if (koFilter && selectedKO.containsSeriesInstanceUIDReference(seriesInstanceUID)) { sopInstanceUIDFilter = selectedKO.getSOPInstanceUIDFilter(); } view2D.setActionsInView(ActionW.FILTERED_SERIES.cmd(), sopInstanceUIDFilter); if (updateImage) { /* * The getFrameIndex() returns a valid index for the current image displayed according to the current * FILTERED_SERIES and the current SortComparator */ int newImageIndex = view2D.getFrameIndex(); if (tiledMode) { newImageIndex = view2D.getTileOffset() + imgSelectionIndex; } if (koFilter && newImageIndex < 0) { if (dicomSeries.size(sopInstanceUIDFilter) > 0 && view2D.getImage() != null) { double[] val = (double[]) view2D.getImage().getTagValue(TagW.SlicePosition); if (val != null) { double location = val[0] + val[1] + val[2]; // Double offset = (Double) view2D.getActionValue(ActionW.STACK_OFFSET.cmd()); // if (offset != null) { // location += offset; // } newImageIndex = dicomSeries.getNearestImageIndex(location, view2D.getTileOffset(), sopInstanceUIDFilter, view2D.getCurrentSortComparator()); } } else { // If there is no more image in KO series filtered then disable the KO_FILTER sopInstanceUIDFilter = null; view2D.setActionsInView(ActionW.KO_FILTER.cmd(), false); view2D.setActionsInView(ActionW.FILTERED_SERIES.cmd(), sopInstanceUIDFilter); newImageIndex = view2D.getFrameIndex(); } } updateImage(view2D, sopInstanceUIDFilter, newImageIndex); } ((View2d) view2D).updateKOButtonVisibleState(); } } private static void updateImage(ViewCanvas<DicomImageElement> view2D, Filter<DicomImageElement> sopInstanceUIDFilter, int newImageIndex) { int imgIndex = newImageIndex < 0 ? 0 : newImageIndex; if (view2D == view2D.getEventManager().getSelectedViewPane()) { /* * Update the sliceAction action according to nearest image when the filter hides the image of the previous * state. And update the action min and max. */ ActionState seqAction = view2D.getEventManager().getAction(ActionW.SCROLL_SERIES); if (seqAction instanceof SliderCineListener) { SliderChangeListener moveTroughSliceAction = (SliderChangeListener) seqAction; moveTroughSliceAction.setSliderMinMaxValue(1, view2D.getSeries().size(sopInstanceUIDFilter), imgIndex + 1); } } DicomImageElement newImage = null; if (view2D.getSeries() != null) { newImage = view2D.getSeries().getMedia(imgIndex, sopInstanceUIDFilter, view2D.getCurrentSortComparator()); } if (newImage != null && !newImage.isImageAvailable()) { newImage.getImage(); } ((View2d) view2D).setImage(newImage); } }