/******************************************************************************* * 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.sr; import java.awt.BorderLayout; import java.awt.Insets; import java.awt.event.KeyEvent; import java.awt.print.PageFormat; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import javax.print.attribute.HashPrintRequestAttributeSet; import javax.print.attribute.PrintRequestAttributeSet; import javax.swing.Action; import javax.swing.ImageIcon; import javax.swing.JComponent; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.osgi.framework.BundleContext; import org.osgi.framework.FrameworkUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.weasis.core.api.explorer.DataExplorerView; import org.weasis.core.api.explorer.ObservableEvent; import org.weasis.core.api.gui.InsertableUtil; import org.weasis.core.api.gui.util.GuiExecutor; import org.weasis.core.api.gui.util.JMVUtils; import org.weasis.core.api.image.GridBagLayoutModel; import org.weasis.core.api.media.data.MediaSeries; import org.weasis.core.api.media.data.MediaSeriesGroup; import org.weasis.core.api.media.data.Series; import org.weasis.core.api.service.BundleTools; import org.weasis.core.ui.docking.DockableTool; import org.weasis.core.ui.docking.UIManager; 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.ImageViewerEventManager; import org.weasis.core.ui.editor.image.ImageViewerPlugin; import org.weasis.core.ui.editor.image.SynchView; import org.weasis.core.ui.editor.image.ViewCanvas; import org.weasis.core.ui.util.DefaultAction; import org.weasis.core.ui.util.ForcedAcceptPrintService; import org.weasis.core.ui.util.Toolbar; import org.weasis.dicom.codec.DicomImageElement; import org.weasis.dicom.codec.DicomSpecialElement; import org.weasis.dicom.codec.TagD; import org.weasis.dicom.codec.TagD.Level; import org.weasis.dicom.explorer.DicomExplorer; import org.weasis.dicom.explorer.DicomFieldsView; import org.weasis.dicom.explorer.DicomModel; public class SRContainer extends ImageViewerPlugin<DicomImageElement> implements PropertyChangeListener { private static final Logger LOGGER = LoggerFactory.getLogger(SRContainer.class); public static final List<SynchView> SYNCH_LIST = Collections.synchronizedList(new ArrayList<SynchView>()); static { SYNCH_LIST.add(SynchView.NONE); } public static final List<GridBagLayoutModel> LAYOUT_LIST = Collections.synchronizedList(new ArrayList<GridBagLayoutModel>()); public static final GridBagLayoutModel VIEWS_1x1 = new GridBagLayoutModel("1x1", //$NON-NLS-1$ "1x1", 1, 1, SRView.class.getName(), new ImageIcon(ImageViewerPlugin.class //$NON-NLS-1$ .getResource("/icon/22x22/layout1x1.png"))); //$NON-NLS-1$ static { LAYOUT_LIST.add(VIEWS_1x1); } // Static tools shared by all the View2dContainer instances, tools are registered when a container is selected // Do not initialize tools in a static block (order initialization issue with eventManager), use instead a lazy // initialization with a method. public static final List<Toolbar> TOOLBARS = Collections.synchronizedList(new ArrayList<Toolbar>(1)); public static final List<DockableTool> TOOLS = Collections.synchronizedList(new ArrayList<DockableTool>(1)); private static volatile boolean initComponents = false; static final ImageViewerEventManager<DicomImageElement> SR_EVENT_MANAGER = new ImageViewerEventManager<DicomImageElement>() { @Override public boolean updateComponentsListener(ViewCanvas<DicomImageElement> defaultView2d) { // Do nothing return true; } @Override public void resetDisplay() { // Do nothing } @Override public void setSelectedView2dContainer(ImageViewerPlugin<DicomImageElement> selectedView2dContainer) { this.selectedView2dContainer = selectedView2dContainer; } @Override public void keyTyped(KeyEvent e) { // Do nothing } @Override public void keyPressed(KeyEvent e) { // Do nothing } @Override public void keyReleased(KeyEvent e) { // Do nothing } }; protected SRView srview; public SRContainer() { this(VIEWS_1x1, null); } public SRContainer(GridBagLayoutModel layoutModel, String uid) { super(SR_EVENT_MANAGER, layoutModel, uid, SRFactory.NAME, SRFactory.ICON, null); setSynchView(SynchView.NONE); if (!initComponents) { initComponents = true; // Add standard toolbars final BundleContext context = FrameworkUtil.getBundle(this.getClass()).getBundleContext(); String bundleName = context.getBundle().getSymbolicName(); String componentName = InsertableUtil.getCName(this.getClass()); String key = "enable"; //$NON-NLS-1$ if (InsertableUtil.getBooleanProperty(BundleTools.SYSTEM_PREFERENCES, bundleName, componentName, InsertableUtil.getCName(SrToolBar.class), key, true)) { TOOLBARS.add(new SrToolBar(10)); } } } @Override public void setSelectedImagePaneFromFocus(ViewCanvas<DicomImageElement> defaultView2d) { setSelectedImagePane(defaultView2d); } @Override public JMenu fillSelectedPluginMenu(JMenu menuRoot) { if (menuRoot != null) { menuRoot.removeAll(); List<Action> actions = getPrintActions(); if (actions != null) { JMenu printMenu = new JMenu(Messages.getString("SRContainer.print")); //$NON-NLS-1$ for (Action action : actions) { JMenuItem item = new JMenuItem(action); printMenu.add(item); } menuRoot.add(printMenu); } } return menuRoot; } @Override public List<DockableTool> getToolPanel() { return TOOLS; } @Override public void setSelected(boolean selected) { if (selected) { eventManager.setSelectedView2dContainer(this); // Send event to select the related patient in Dicom Explorer. DataExplorerView dicomView = UIManager.getExplorerplugin(DicomExplorer.NAME); if (dicomView != null && dicomView.getDataExplorerModel() instanceof DicomModel) { dicomView.getDataExplorerModel().firePropertyChange( new ObservableEvent(ObservableEvent.BasicAction.SELECT, this, null, getGroupID())); } } else { eventManager.setSelectedView2dContainer(null); } } @Override public void close() { SRFactory.closeSeriesViewer(this); super.close(); GuiExecutor.instance().execute(() -> { if (srview != null) { srview.dispose(); } }); } @Override public void propertyChange(PropertyChangeEvent evt) { if (evt instanceof ObservableEvent) { ObservableEvent event = (ObservableEvent) evt; ObservableEvent.BasicAction action = event.getActionCommand(); Object newVal = event.getNewValue(); if (ObservableEvent.BasicAction.REMOVE.equals(action)) { if (newVal instanceof MediaSeriesGroup) { MediaSeriesGroup group = (MediaSeriesGroup) newVal; // Patient Group if (TagD.getUID(Level.PATIENT).equals(group.getTagID())) { if (group.equals(getGroupID())) { // Close the content of the plug-in close(); handleFocusAfterClosing(); } } // Study Group else if (TagD.getUID(Level.STUDY).equals(group.getTagID())) { if (event.getSource() instanceof DicomModel) { DicomModel model = (DicomModel) event.getSource(); if (srview != null && group.equals(model.getParent(srview.getSeries(), DicomModel.study))) { close(); handleFocusAfterClosing(); } } } // Series Group else if (TagD.getUID(Level.SERIES).equals(group.getTagID())) { if (srview != null && srview.getSeries() == newVal) { close(); handleFocusAfterClosing(); } } } } } } @Override public int getViewTypeNumber(GridBagLayoutModel layout, Class<?> defaultClass) { return 0; } @Override public boolean isViewType(Class<?> defaultClass, String type) { if (defaultClass != null) { try { Class<?> clazz = Class.forName(type); return defaultClass.isAssignableFrom(clazz); } catch (Exception e) { LOGGER.error("Checking view type", e); //$NON-NLS-1$ } } return false; } @Override public ViewCanvas<DicomImageElement> createDefaultView(String classType) { return null; } @Override public JComponent createUIcomponent(String clazz) { try { // FIXME use classloader.loadClass or injection Class<?> cl = Class.forName(clazz); JComponent component = (JComponent) cl.newInstance(); if (component instanceof SeriesViewerListener) { eventManager.addSeriesViewerListener((SeriesViewerListener) component); } if (component instanceof SRView) { srview = (SRView) component; } return component; } catch (Exception e) { LOGGER.error("Cannot create {}", clazz, e); //$NON-NLS-1$ } return null; } @Override public synchronized List<Toolbar> getToolBar() { return TOOLBARS; } @Override public List<Action> getPrintActions() { final String title = Messages.getString("SRContainer.print_layout"); //$NON-NLS-1$ return Arrays.asList( new DefaultAction(title, new ImageIcon(ImageViewerPlugin.class.getResource("/icon/16x16/printer.png")), //$NON-NLS-1$ event -> printCurrentView())); } void displayHeader() { if (srview != null) { JFrame frame = new JFrame(org.weasis.dicom.explorer.Messages.getString("DicomExplorer.dcmInfo")); //$NON-NLS-1$ frame.setSize(500, 630); DicomFieldsView view = new DicomFieldsView(); view.changingViewContentEvent(new SeriesViewerEvent(this, srview.getSeries(), DicomModel.getFirstSpecialElement(srview.getSeries(), DicomSpecialElement.class), EVENT.SELECT)); JPanel panel = new JPanel(); panel.setLayout(new BorderLayout()); panel.add(view); frame.getContentPane().add(panel); frame.setAlwaysOnTop(true); JMVUtils.showCenterScreen(frame, srview); } } void printCurrentView() { if (srview != null) { PrintRequestAttributeSet aset = new HashPrintRequestAttributeSet(); PrinterJob pj = PrinterJob.getPrinterJob(); // Get page format from the printer if (pj.printDialog(aset)) { PageFormat pageFormat = pj.defaultPage(); // Force to print in black and white EditorPanePrinter pnlPreview = new EditorPanePrinter(srview.getHtmlPanel(), pageFormat, new Insets(18, 18, 18, 18)); pj.setPageable(pnlPreview); try { pj.print(); } catch (PrinterException e) { // check for the annoying 'Printer is not accepting job' error. if (e.getMessage().indexOf("accepting job") != -1) { //$NON-NLS-1$ // recommend prompting the user at this point if they want to force it // so they'll know there may be a problem. int response = JOptionPane.showConfirmDialog(null, org.weasis.core.ui.Messages.getString("ImagePrint.issue_desc"), //$NON-NLS-1$ org.weasis.core.ui.Messages.getString("ImagePrint.status"), JOptionPane.YES_NO_OPTION, //$NON-NLS-1$ JOptionPane.WARNING_MESSAGE); if (response == 0) { try { // try printing again but ignore the not-accepting-jobs attribute ForcedAcceptPrintService.setupPrintJob(pj); // add secret ingredient pj.print(aset); LOGGER.info("Bypass Printer is not accepting job"); //$NON-NLS-1$ } catch (PrinterException ex) { LOGGER.error("Printer exception", ex); //$NON-NLS-1$ } } } else { LOGGER.error("Print exception", e); //$NON-NLS-1$ } } } } } public Series<?> getSeries() { if (srview != null) { return srview.getSeries(); } return null; } @Override public void addSeries(MediaSeries<DicomImageElement> sequence) { if (srview != null && sequence instanceof Series && srview.getSeries() != sequence) { srview.setSeries((Series<DicomImageElement>) sequence); } } @Override public void addSeriesList(List<MediaSeries<DicomImageElement>> seriesList, boolean removeOldSeries) { if (seriesList != null && !seriesList.isEmpty()) { addSeries(seriesList.get(0)); } } @Override public void selectLayoutPositionForAddingSeries(List<MediaSeries<DicomImageElement>> seriesList) { // Do it in addSeries() } @Override public List<SynchView> getSynchList() { return SYNCH_LIST; } @Override public List<GridBagLayoutModel> getLayoutList() { return LAYOUT_LIST; } }