/* SignalView.java created 2007-09-19 * */ package org.signalml.app.view.signal; import static org.signalml.app.util.i18n.SvarogI18n._; import static org.signalml.app.util.i18n.SvarogI18n._R; import java.awt.BorderLayout; import java.awt.CardLayout; import java.awt.Color; import java.awt.Component; import java.awt.Container; import java.awt.Cursor; import java.awt.Dimension; import java.awt.GridLayout; import java.awt.Point; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.geom.Point2D; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.InvalidClassException; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.SortedSet; import java.util.TreeSet; import javax.swing.AbstractAction; import javax.swing.ActionMap; import javax.swing.Box; import javax.swing.ButtonGroup; import javax.swing.ButtonModel; import javax.swing.InputMap; import javax.swing.JButton; import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JSlider; import javax.swing.JSplitPane; import javax.swing.JToggleButton; import javax.swing.JToolBar; import javax.swing.JViewport; import javax.swing.KeyStroke; import javax.swing.SwingUtilities; import javax.swing.Timer; import javax.swing.border.CompoundBorder; import javax.swing.border.EmptyBorder; import javax.swing.border.LineBorder; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.apache.log4j.Logger; import org.signalml.app.action.DisplayClockTimeAction; import org.signalml.app.action.SnapToPageAction; import org.signalml.app.action.document.monitor.StartMonitorRecordingAction; import org.signalml.app.action.document.monitor.StopMonitorRecordingAction; import org.signalml.app.action.montage.ApplyDefaultMontageAction; import org.signalml.app.action.montage.EditSignalMontageAction; import org.signalml.app.action.selector.ActionFocusListener; import org.signalml.app.action.selector.ActionFocusManager; import org.signalml.app.action.selector.ActionFocusSupport; import org.signalml.app.action.selector.MontageFocusSelector; import org.signalml.app.action.selector.SignalPlotFocusSelector; import org.signalml.app.action.selector.TagFocusSelector; import org.signalml.app.action.selector.TagStyleFocusSelector; import org.signalml.app.action.signal.EditSignalParametersAction; import org.signalml.app.action.signal.PreciseSelectionAction; import org.signalml.app.action.signal.SignalFilterSwitchAction; import org.signalml.app.action.tag.CloseTagAction; import org.signalml.app.action.tag.EditTagAnnotationAction; import org.signalml.app.action.tag.NewTagAction; import org.signalml.app.action.tag.OpenTagAction; import org.signalml.app.action.tag.RemoveTagAction; import org.signalml.app.action.tag.SaveTagAction; import org.signalml.app.action.tag.SaveTagAsAction; import org.signalml.app.action.tag.TagSelectionAction; import org.signalml.app.config.ApplicationConfiguration; import org.signalml.app.config.preset.PresetManagerAdapter; import org.signalml.app.config.preset.PresetManagerEvent; import org.signalml.app.config.preset.PresetManagerListener; import org.signalml.app.document.DocumentFlowIntegrator; import org.signalml.app.document.TagDocument; import org.signalml.app.document.signal.SignalDocument; import org.signalml.app.model.components.LogarithmicJSlider; import org.signalml.app.model.montage.MontagePresetManager; import org.signalml.app.util.IconUtils; import org.signalml.app.util.ResnapToPageRunnable; import org.signalml.app.view.common.components.LockableJSplitPane; import org.signalml.app.view.common.components.panels.TitledSliderPanel; import org.signalml.app.view.common.dialogs.errors.Dialogs; import org.signalml.app.view.document.monitor.MonitorRecordingDurationPanel; import org.signalml.app.view.document.monitor.StartMonitorRecordingDialog; import org.signalml.app.view.montage.SignalMontageDialog; import org.signalml.app.view.signal.popup.ChannelOptionsPopupDialog; import org.signalml.app.view.signal.popup.SignalPlotOptionsPopupDialog; import org.signalml.app.view.signal.popup.SlavePlotSettingsPopupDialog; import org.signalml.app.view.signal.popup.ZoomSettingsPopupDialog; import org.signalml.app.view.tag.EditTagAnnotationDialog; import org.signalml.app.view.tag.EditTagDescriptionDialog; import org.signalml.app.view.tag.NewTagDialog; import org.signalml.app.view.tag.TagIconProducer; import org.signalml.app.view.tag.TagStylePaletteDialog; import org.signalml.app.view.tag.TagStyleSelector; import org.signalml.app.view.tag.TagStyleToolBar; import org.signalml.app.view.workspace.ViewerFileChooser; import org.signalml.domain.montage.Montage; import org.signalml.domain.signal.samplesource.MultichannelSampleSource; import org.signalml.domain.signal.space.SignalSpaceConstraints; import org.signalml.domain.tag.StyledTagSet; import org.signalml.domain.tag.TagDifference; import org.signalml.domain.tag.TagDifferenceDetector; import org.signalml.domain.tag.TagDifferenceSet; import org.signalml.domain.tag.TagEvent; import org.signalml.domain.tag.TagListener; import org.signalml.domain.tag.TagStyleEvent; import org.signalml.domain.tag.TagStyleListener; import org.signalml.exception.SanityCheckException; import org.signalml.plugin.export.SignalMLException; import org.signalml.plugin.export.signal.Document; import org.signalml.plugin.export.signal.ExportedSignalSelection; import org.signalml.plugin.export.signal.SignalSelection; import org.signalml.plugin.export.signal.SignalSelectionType; import org.signalml.plugin.export.signal.SignalTool; import org.signalml.plugin.export.signal.Tag; import org.signalml.plugin.export.signal.TagStyle; import org.signalml.plugin.export.view.DocumentView; import org.signalml.plugin.export.view.ExportedPositionedTag; import org.signalml.plugin.export.view.ExportedSignalPlot; import org.signalml.plugin.export.view.ExportedSignalView; import org.signalml.plugin.impl.PluginAccessClass; import org.signalml.util.Util; /** SignalView * * * @author Michal Dobaczewski © 2007-2008 CC Otwarte Systemy Komputerowe Sp. z o.o. */ public class SignalView extends DocumentView implements PropertyChangeListener, TagListener, TagStyleListener, TagFocusSelector, TagStyleFocusSelector, SignalPlotFocusSelector, MontageFocusSelector, ExportedSignalView { private static final long serialVersionUID = 1L; protected static final Logger logger = Logger.getLogger(SignalView.class); private ActionFocusSupport afSupport = new ActionFocusSupport(this); private boolean closed = false; private ApplicationConfiguration applicationConfig; private JToolBar mainToolBar; private JToolBar tagToolBar; private SignalDocument document; private LinkedList<SignalPlot> plots; private LinkedList<SignalPlotPanel> plotPanels; private LinkedList<SignalPlotScrollPane> scrollPanes; private LinkedList<SignalPlotColumnHeader> columnHeaders; private LinkedList<SignalPlotRowHeader> rowHeaders; private LinkedList<SignalPlotCorner> corners; private LockableJSplitPane plotSplitPane; private JPanel contentPane; private FocusListener plotFocusListener; private MouseListener plotActivationMouseListener; private SignalPlot activePlot = null; private SignalSelection signalSelection; private PositionedTag tagSelection; private SignalPlot signalSelectionPlot; private SignalPlot tagSelectionPlot; private PlotScrollingCoordinator scrollingCoordinator; private JSlider timeScaleSlider; private JSlider valueScaleSlider; private JSlider channelHeightSlider; private ButtonGroup toolButtonGroup; private JButton plotOptionsButton; private JToggleButton selectToolButton; private JToggleButton moveToolButton; private JToggleButton selectPageToolButton; private JToggleButton selectBlockToolButton; private JToggleButton selectChannelToolButton; private JToggleButton rulerToolButton; private JToggleButton tagPageToolButton; private JToggleButton tagBlockToolButton; private JToggleButton tagChannelToolButton; private JToggleButton zoomSignalToolButton; private SignalTool currentSignalTool; private SelectTagSignalTool selectTagTool; private MoveSignalSignalTool moveSignalTool; private SelectPageSignalTool selectPageTool; private SelectBlockSignalTool selectBlockTool; private SelectChannelSignalTool selectChannelTool; private RulerSignalTool rulerSignalTool; private TagPageSignalTool tagPageSignalTool; private TagBlockSignalTool tagBlockSignalTool; private TagChannelSignalTool tagChannelSignalTool; private ZoomSignalTool zoomSignalTool; private Map<ButtonModel,SignalTool> toolMap = new HashMap<ButtonModel,SignalTool>(); private DocumentFlowIntegrator documentFlowIntegrator; private NewTagDialog newTagDialog; private ViewerFileChooser fileChooser; private SignalSelectionDialog signalSelectionDialog; private StartMonitorRecordingDialog startMonitorRecordingDialog; private SignalParametersDialog signalParametersDialog; private SignalMontageDialog signalMontageDialog; private EditTagAnnotationDialog editTagAnnotationDialog; private TagStylePaletteDialog tagStylePaletteDialog; private EditTagDescriptionDialog editTagDescriptionDialog; private NewTagAction newTagAction; private OpenTagAction openTagAction; private CloseTagAction closeTagAction; private SaveTagAction saveTagAction; private SaveTagAsAction saveTagAsAction; /** * An {@link Action} responsible for starting a monitor recording. */ private StartMonitorRecordingAction startMonitorRecordingAction; /** * An {@link Action} responsible for stopping an ongoing monitor * recording. */ private StopMonitorRecordingAction stopMonitorRecordingAction; private MonitorRecordingDurationPanel monitorRecordingDurationPanel; private EditSignalParametersAction editSignalParametersAction; private EditSignalMontageAction editSignalMontageAction; private ApplyDefaultMontageAction applyDefaultMontageAction; private PreciseSelectionAction preciseSelectionAction; private TagSelectionAction tagSelectionAction; private RemoveTagAction removeTagAction; private DisplayClockTimeAction displayClockTimeAction; private EditTagAnnotationAction editTagAnnotationAction; private SnapToPageAction snapToPageAction; private SignalFilterSwitchAction signalFilterSwitchAction; private boolean displayClockTime = false; private boolean snapToPageMode = false; private boolean deferredSnapToPage = false; private MontagePresetManager montagePresetManager; private PresetManagerListener montagePresetManagerListener; private SignalPlotOptionsPopupDialog signalPlotOptionsPopupDialog; private ZoomSettingsPopupDialog zoomSettingsDialog; private SlavePlotSettingsPopupDialog slavePlotSettingsPopupDialog; private ChannelOptionsPopupDialog channelOptionsPopupDialog; private CardLayout tagToolBarLayout; private JPanel tagToolBarPanel; private Map<String,TagStyleToolBar> styleToolBarMap; private ActionFocusManager actionFocusManager; private TagIconProducer tagIconProducer; private HypnogramPlot hypnogramPlot = null; // private ZoomMouseWheelListener zoomMouseWheelListener; private SignalToolForwardingMouseAdapter toolMouseAdapter; private SignalToolForwardingMouseAdapter columnToolMouseAdapter; private SignalToolForwardingMouseAdapter rowToolMouseAdapter; private GridLayout plotPanelGridLayout; private JPanel plotPanel; private TagDifferenceDetector tagDifferenceDetector; private TagDocument[] comparedTags; private TagDifferenceSet differenceSet; private HashMap<KeyStroke,TagStyle> lastStylesByKeyStrokes; public SignalView(SignalDocument document) { super(new BorderLayout()); this.document = document; document.addPropertyChangeListener(this); } public void initialize() throws SignalMLException { tagIconProducer = new TagIconProducer(); plots = new LinkedList<SignalPlot>(); plotPanels = new LinkedList<SignalPlotPanel>(); scrollPanes = new LinkedList<SignalPlotScrollPane>(); columnHeaders = new LinkedList<SignalPlotColumnHeader>(); rowHeaders = new LinkedList<SignalPlotRowHeader>(); corners = new LinkedList<SignalPlotCorner>(); plotFocusListener = new FocusListener() { @Override public void focusGained(FocusEvent e) { logger.debug("Focus gained by plot [" + e.getSource().toString() + "]"); } @Override public void focusLost(FocusEvent e) { logger.debug("Focus lost by plot [" + e.getSource().toString() + "]"); } }; plotActivationMouseListener = new MouseAdapter() { @Override public void mousePressed(MouseEvent e) { SignalPlot plot = (SignalPlot) e.getSource(); setActivePlot(plot); plot.requestFocusInWindow(); } }; addComponentListener(new ComponentAdapter() { @Override public void componentShown(ComponentEvent e) { if (deferredSnapToPage) { snapPageToView(); } if (activePlot != null) { activePlot.requestFocusInWindow(); } } }); // zoomMouseWheelListener = new ZoomMouseWheelListener(this); toolMouseAdapter = new SignalToolForwardingMouseAdapter(); columnToolMouseAdapter = new SignalToolForwardingMouseAdapter(true,false); rowToolMouseAdapter = new SignalToolForwardingMouseAdapter(false,true); hypnogramPlot = new HypnogramPlot(this); document.addPropertyChangeListener(hypnogramPlot); plotPanel = new JPanel(); plotPanelGridLayout = new GridLayout(1,0,0,5); plotPanel.setLayout(new PlotPanelLayout()); contentPane = new JPanel(new BorderLayout()); plotSplitPane = new LockableJSplitPane(JSplitPane.VERTICAL_SPLIT, true, null, plotPanel); plotSplitPane.setResizeWeight(0.5); plotSplitPane.setDividerSize(8); plotSplitPane.setOneTouchExpandable(false); plotSplitPane.setBorder(null); SignalPlot masterPlot = createSignalPlot(null); masterPlot.getViewport().addComponentListener(new ComponentAdapter() { @Override public void componentResized(ComponentEvent e) { if (snapToPageMode) { SwingUtilities.invokeLater(new ResnapToPageRunnable(SignalView.this)); } } }); scrollingCoordinator = new PlotScrollingCoordinator(plots.getFirst()); buildMainToolBar(); buildTagToolBar(); buildTools(); JPanel hypnogramPanel = new JPanel(new BorderLayout()); hypnogramPanel.setBorder(new CompoundBorder( new EmptyBorder(3,0,5,0), new LineBorder(Color.LIGHT_GRAY) )); hypnogramPanel.add(hypnogramPlot, BorderLayout.CENTER); contentPane.add(hypnogramPanel, BorderLayout.NORTH); add(mainToolBar, BorderLayout.NORTH); add(tagToolBar, BorderLayout.WEST); add(contentPane, BorderLayout.CENTER); KeyStroke del = KeyStroke.getKeyStroke("DELETE"); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(del, "removeTagOrEraseSelection"); getActionMap().put("removeTagOrEraseSelection", new DelKeyRedirectAction()); KeyStroke plus = KeyStroke.getKeyStroke('+'); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(plus, "pageForward"); getActionMap().put("pageForward", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (activePlot != null) { activePlot.pageForward(); } } }); KeyStroke minus = KeyStroke.getKeyStroke('-'); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(minus, "pageBackward"); getActionMap().put("pageBackward", new AbstractAction() { private static final long serialVersionUID = 1L; @Override public void actionPerformed(ActionEvent e) { if (activePlot != null) { activePlot.pageBackward(); } } }); // cycle input map to protect it from overwriting default bindings by tag bindings InputMap map = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); InputMap topMap = new InputMap(); topMap.setParent(map); setInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT, topMap); } private SignalPlot createSignalPlot(SignalPlot masterPlot) throws SignalMLException { SignalPlot plot = new SignalPlot(document, this, masterPlot); SignalPlotPopupProvider signalPlotPopupProvider = new SignalPlotPopupProvider(plot); signalPlotPopupProvider.setTagIconProducer(tagIconProducer); signalPlotPopupProvider.setPreciseSelectionAction(getPreciseSelectionAction()); signalPlotPopupProvider.setTagSelectionAction(getTagSelectionAction()); signalPlotPopupProvider.setRemoveTagAction(getRemoveTagAction()); signalPlotPopupProvider.setEditTagAnnotationAction(getEditTagAnnotationAction()); plot.setPopupMenuProvider(signalPlotPopupProvider); plot.initialize(); plots.add(plot); plot.addMouseListener(plotActivationMouseListener); SignalPlotScrollPane scrollPane = new SignalPlotScrollPane(plot,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS); if (document.getTagDocuments().size() > 1) { scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); } else { scrollPane.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE); } plot.setViewport(scrollPane.getViewport()); SignalPlotColumnHeader columnHeader = plot.getSignalPlotColumnHeader(); SignalPlotRowHeader rowHeader = plot.getSignalPlotRowHeader(); SignalPlotCorner corner = plot.getSignalPlotCorner(); scrollPane.setColumnHeaderView(columnHeader); scrollPane.setRowHeaderView(rowHeader); scrollPane.setCorner(JScrollPane.UPPER_LEFT_CORNER, corner); scrollPane.setWheelScrollingEnabled(false); scrollPane.setMinimumSize(new Dimension(200,200)); scrollPanes.add(scrollPane); columnHeaders.add(columnHeader); rowHeaders.add(rowHeader); corners.add(corner); // plot.addMouseWheelListener(zoomMouseWheelListener); plot.addMouseMotionListener(toolMouseAdapter); plot.addMouseListener(toolMouseAdapter); plot.addMouseWheelListener(toolMouseAdapter); columnHeader.addMouseMotionListener(columnToolMouseAdapter); columnHeader.addMouseListener(columnToolMouseAdapter); columnHeader.addMouseWheelListener(columnToolMouseAdapter); rowHeader.addMouseMotionListener(rowToolMouseAdapter); rowHeader.addMouseListener(rowToolMouseAdapter); rowHeader.addMouseWheelListener(rowToolMouseAdapter); plot.setAutoscrolls(true); SignalPlotPanel signalPlotPanel = new SignalPlotPanel(plot,scrollPane); plotPanels.add(signalPlotPanel); if (masterPlot == null) { plot.getViewport().addChangeListener(hypnogramPlot); contentPane.add(scrollPane, BorderLayout.CENTER); } else { scrollingCoordinator.addPlot(plot); plotPanelGridLayout.setRows(plots.size()-1); plotPanel.add(signalPlotPanel); if (plots.size() <= 2) { // if this is the first slave plot contentPane.remove(scrollPanes.getFirst()); plotSplitPane.setLeftComponent(scrollPanes.getFirst()); contentPane.add(plotSplitPane, BorderLayout.CENTER); } // else is not needed } plot.addFocusListener(plotFocusListener); setActivePlot(plot); plot.requestFocusInWindow(); return plot; } public SignalPlot addSlavePlot(SignalPlot masterPlot) { SignalPlot plot; try { plot = createSignalPlot(masterPlot); } catch (SignalMLException ex) { logger.error("Failed to create signal plot", ex); Dialogs.showExceptionDialog((Window) getTopLevelAncestor(), ex); return null; } if (plots.size() >=4) { ((MasterSignalPlotCorner) corners.getFirst()).setAddSlavePlotEnabled(false); } else { ((MasterSignalPlotCorner) corners.getFirst()).setAddSlavePlotEnabled(true); } revalidate(); repaint(); return plot; } public void removeSlavePlot(SignalPlot slavePlot) { if (!plots.contains(slavePlot)) { logger.warn("WARNING: Plot not in plots"); return; } int index = plots.indexOf(slavePlot); if (index == 0) { logger.warn("WARNING: Cannot remove master plot"); return; } if (signalSelectionPlot == slavePlot) { clearSignalSelection(); } if (tagSelectionPlot == slavePlot) { clearTagSelection(); } scrollingCoordinator.removePlot(slavePlot); plots.remove(index); plotPanels.remove(index); scrollPanes.remove(index); columnHeaders.remove(index); rowHeaders.remove(index); corners.remove(index); plotPanel.remove(index-1); plotPanelGridLayout.setRows(Math.max(1, plots.size()-1)); if (plots.size() == 1) { contentPane.remove(plotSplitPane); plotSplitPane.setLeftComponent(null); contentPane.add(scrollPanes.getFirst(), BorderLayout.CENTER); } if (slavePlot == activePlot) { setActivePlot(getMasterPlot()); } slavePlot.destroy(); if (plots.size() >=4) { ((MasterSignalPlotCorner) corners.getFirst()).setAddSlavePlotEnabled(false); } else { ((MasterSignalPlotCorner) corners.getFirst()).setAddSlavePlotEnabled(true); } revalidate(); repaint(); } public int getSynchronizedRowHeaderWidth() { int maxWidth = 0; int width; for (SignalPlotRowHeader rowHeader : rowHeaders) { width = rowHeader.getPreferredWidth(); if (maxWidth < width) { maxWidth = width; } } for (SignalPlotCorner corner : corners) { width = corner.getPreferredWidth(); if (maxWidth < width) { maxWidth = width; } } return maxWidth; } @Override public TagStyle getActiveTagStyle() { SignalSelectionType type = getCurrentTagType(); if (type != null) { return getCurrentTagStyle(type); } return null; } @Override public SignalDocument getActiveSignalDocument() { return document; } @Override public TagDocument getActiveTagDocument() { return document.getActiveTag(); } @Override public Document getActiveDocument() { return document; } @Override public PositionedTag getActiveTag() { return getTagSelection(); } @Override public SignalPlot getActiveSignalPlot() { return activePlot; } @Override public Montage getActiveMontage() { return actionFocusManager.getActiveMontage(); } public void setActivePlot(SignalPlot activePlot) { if (this.activePlot != activePlot) { if (this.activePlot != null) { this.activePlot.getSignalPlotRowHeader().setActive(false); } this.activePlot = activePlot; if (activePlot != null) { activePlot.getSignalPlotRowHeader().setActive(true); } logger.debug("Plot [" + activePlot.toString() + "] activated"); afSupport.fireActionFocusChanged(); } } @Override public void addActionFocusListener(ActionFocusListener listener) { afSupport.addActionFocusListener(listener); } @Override public void removeActionFocusListener(ActionFocusListener listener) { afSupport.removeActionFocusListener(listener); } @Override public SignalSelection getSignalSelection() { return signalSelection; } public SignalPlot getSignalSelectionPlot() { return signalSelectionPlot; } public SignalSelection getSignalSelection(SignalPlot plot) { if (signalSelection == null || signalSelectionPlot != plot) { return null; } return signalSelection; } public void setSignalSelection(SignalPlot plot, SignalSelection signalSelection) { if ((plot != this.signalSelectionPlot) || !Util.equalsWithNulls(this.signalSelection, signalSelection)) { SignalSelection oldSelection = this.signalSelection; SignalPlot oldPlot = this.signalSelectionPlot; this.signalSelection = signalSelection; this.signalSelectionPlot = plot; if (oldSelection != null) { oldPlot.repaintSelectionBounds(oldSelection); } if (signalSelection != null) { plot.repaintSelectionBounds(signalSelection); clearTagSelection(); } afSupport.fireActionFocusChanged(); } } @Override public void clearSignalSelection() { setSignalSelection(null,null); } @Override public PositionedTag getTagSelection() { return tagSelection; } public SignalPlot getTagSelectionPlot() { return tagSelectionPlot; } public PositionedTag getTagSelection(SignalPlot plot) { if (tagSelection == null || tagSelectionPlot != plot) { return null; } return tagSelection; } public void setTagSelection(SignalPlot plot, PositionedTag tagSelection) { if ((plot != this.signalSelectionPlot) || !Util.equalsWithNulls(this.tagSelection, tagSelection)) { int tagCnt = document.getTagDocuments().size(); PositionedTag oldSelection = this.tagSelection; SignalPlot oldPlot = this.tagSelectionPlot; this.tagSelection = tagSelection; this.tagSelectionPlot = plot; if (oldSelection != null) { // when tagCnt is 0 division by zero inside if (tagCnt > 0) oldPlot.repaintTagBounds(oldSelection, tagCnt); } if (tagSelection != null) { // when tagCnt is 0 division by zero inside if (tagCnt > 0) plot.repaintTagBounds(tagSelection, tagCnt); clearSignalSelection(); } afSupport.fireActionFocusChanged(); } } @Override public void clearTagSelection() { setTagSelection(null, null); } public LockableJSplitPane getPlotSplitPane() { return plotSplitPane; } private void buildTools() { selectTagTool = new SelectTagSignalTool(this); moveSignalTool = new MoveSignalSignalTool(this); selectPageTool = new SelectPageSignalTool(this); selectBlockTool = new SelectBlockSignalTool(this); selectChannelTool = new SelectChannelSignalTool(this); rulerSignalTool = new RulerSignalTool(this); tagPageSignalTool = new TagPageSignalTool(this); tagBlockSignalTool = new TagBlockSignalTool(this); tagChannelSignalTool = new TagChannelSignalTool(this); zoomSignalTool = new ZoomSignalTool(this); zoomSignalTool.setSettings(applicationConfig.getZoomSignalSettings()); toolMouseAdapter.setSelectTagSignalTool(selectTagTool); columnToolMouseAdapter.setSelectTagSignalTool(selectTagTool); currentSignalTool = selectTagTool; toolMouseAdapter.setSignalTool(currentSignalTool); columnToolMouseAdapter.setSignalTool(currentSignalTool); rowToolMouseAdapter.setSignalTool(currentSignalTool); toolMap.put(selectToolButton.getModel(), selectTagTool); toolMap.put(moveToolButton.getModel(), moveSignalTool); toolMap.put(selectPageToolButton.getModel(), selectPageTool); toolMap.put(selectBlockToolButton.getModel(), selectBlockTool); toolMap.put(selectChannelToolButton.getModel(), selectChannelTool); toolMap.put(rulerToolButton.getModel(), rulerSignalTool); toolMap.put(tagPageToolButton.getModel(), tagPageSignalTool); toolMap.put(tagBlockToolButton.getModel(), tagBlockSignalTool); toolMap.put(tagChannelToolButton.getModel(), tagChannelSignalTool); toolMap.put(zoomSignalToolButton.getModel(), zoomSignalTool); toolButtonGroup = new ButtonGroup(); toolButtonGroup.add(selectToolButton); toolButtonGroup.add(moveToolButton); toolButtonGroup.add(selectPageToolButton); toolButtonGroup.add(selectBlockToolButton); toolButtonGroup.add(selectChannelToolButton); toolButtonGroup.add(rulerToolButton); toolButtonGroup.add(tagPageToolButton); toolButtonGroup.add(tagBlockToolButton); toolButtonGroup.add(tagChannelToolButton); toolButtonGroup.add(zoomSignalToolButton); ActionListener toolSelectionListener = new ToolSelectionListener(); selectToolButton.addActionListener(toolSelectionListener); moveToolButton.addActionListener(toolSelectionListener); selectPageToolButton.addActionListener(toolSelectionListener); selectBlockToolButton.addActionListener(toolSelectionListener); selectChannelToolButton.addActionListener(toolSelectionListener); rulerToolButton.addActionListener(toolSelectionListener); tagPageToolButton.addActionListener(toolSelectionListener); tagBlockToolButton.addActionListener(toolSelectionListener); tagChannelToolButton.addActionListener(toolSelectionListener); zoomSignalToolButton.addActionListener(toolSelectionListener); PluginAccessClass.getGUIImpl().registerSignalTools(toolMap, toolButtonGroup, toolSelectionListener, this); selectToolButton.setSelected(true); } private void buildMainToolBar() { SignalPlot plot = plots.getFirst(); mainToolBar = new JToolBar(); mainToolBar.setFloatable(false); timeScaleSlider = new JSlider(plot.getTimeScaleRangeModel()) { private static final long serialVersionUID = 1L; @Override public String getToolTipText(MouseEvent ev) { return _R("{0} pixels/sample", ((double) getValue())/1000); } }; timeScaleSlider.setToolTipText(""); Dimension d = timeScaleSlider.getPreferredSize(); d.width = 100; timeScaleSlider.setPreferredSize(d); timeScaleSlider.setMinimumSize(d); timeScaleSlider.setMaximumSize(d); valueScaleSlider = new LogarithmicJSlider(plot.getValueScaleRangeModel()) { private static final long serialVersionUID = 1L; @Override public String getToolTipText(MouseEvent ev) { return getValue() + "%"; } }; valueScaleSlider.setToolTipText(""); valueScaleSlider.setPreferredSize(d); valueScaleSlider.setMinimumSize(d); valueScaleSlider.setMaximumSize(d); channelHeightSlider = new JSlider(plot.getChannelHeightRangeModel()) { private static final long serialVersionUID = 1L; @Override public String getToolTipText(MouseEvent ev) { return _R("{0} px", getValue()); } }; channelHeightSlider.setToolTipText(""); channelHeightSlider.setPreferredSize(d); channelHeightSlider.setMinimumSize(d); channelHeightSlider.setMaximumSize(d); channelHeightSlider.addChangeListener(new ChangeListener() { @Override public void stateChanged(ChangeEvent e) { if (plots.size() > 1) { plotPanel.revalidate(); } } }); plotOptionsButton = new JButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/plotoptions.png")); plotOptionsButton.setToolTipText(_("Change plot options")); plotOptionsButton.addActionListener(new PlotOptionsButtonListener()); moveToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/hand.png")); moveToolButton.setToolTipText(_("Move signal with the mouse")); selectToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/arrow.png")); selectToolButton.setToolTipText(_("Select tags")); selectPageToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/pageselection.png")); selectPageToolButton.setToolTipText(_("Select signal pages")); selectBlockToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/blockselection.png")); selectBlockToolButton.setToolTipText(_("Select signal blocks")); selectChannelToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/channelselection.png")); selectChannelToolButton.setToolTipText(_("Select single channel or multichannel custom size signal fragments")); zoomSignalToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/zoom.png")); zoomSignalToolButton.setToolTipText(_("Magnify the signal (for settings press and hold the mouse button here)")); zoomSignalToolButton.addMouseListener(new ZoomSignalToolButtonMouseListener()); rulerToolButton = new JToggleButton(IconUtils.loadClassPathIcon("org/signalml/app/icon/ruler.png")); rulerToolButton.setToolTipText(_("Measure the signal")); mainToolBar.add(selectToolButton); selectToolButton.setSelected(true); mainToolBar.add(moveToolButton); mainToolBar.add(selectPageToolButton); mainToolBar.add(selectBlockToolButton); mainToolBar.add(selectChannelToolButton); mainToolBar.add(zoomSignalToolButton); mainToolBar.add(rulerToolButton); PluginAccessClass.getGUIImpl().toolsToMainMenu(mainToolBar, this); mainToolBar.addSeparator(); mainToolBar.add(getNewTagAction()); mainToolBar.add(getOpenTagAction()); mainToolBar.add(getSaveTagAction()); mainToolBar.add(getSaveTagAsAction()); mainToolBar.add(getCloseTagAction()); mainToolBar.add(Box.createHorizontalGlue()); mainToolBar.add(getMonitorRecordingDurationPanel()); mainToolBar.add(getStartMonitorRecordingAction()); mainToolBar.add(getStopMonitorRecordingAction()); PluginAccessClass.getGUIImpl().addToMainSignalToolBar(mainToolBar); mainToolBar.add(Box.createHorizontalGlue()); //mainToolBar.add(getPreciseSelectionAction()); mainToolBar.addSeparator(); mainToolBar.add(new TitledSliderPanel(_("Time scale"), timeScaleSlider)); JToggleButton snapToPageButton = new JToggleButton(getSnapToPageAction()); snapToPageButton.setHideActionText(true); mainToolBar.add(snapToPageButton); JToggleButton displayClockTimeButton = new JToggleButton(getDisplayClockTimeAction()); displayClockTimeButton.setHideActionText(true); mainToolBar.add(displayClockTimeButton); mainToolBar.addSeparator(); mainToolBar.add(new TitledSliderPanel(_("Value scale"), valueScaleSlider)); mainToolBar.add(new TitledSliderPanel(_("Channel height"), channelHeightSlider)); mainToolBar.addSeparator(); mainToolBar.addSeparator(); mainToolBar.add(getEditSignalParametersAction()); mainToolBar.add(getEditSignalMontageAction()); mainToolBar.add(getApplyDefaultMontageAction()); mainToolBar.add(plotOptionsButton); JToggleButton filterSwitchButton = new JToggleButton(getFilterSwitchAction()); filterSwitchButton.setHideActionText(true); filterSwitchButton.setSelectedIcon(IconUtils.loadClassPathIcon("org/signalml/app/icon/filteron.png")); filterSwitchButton.setSelected(document.getMontage().isFiltered()); mainToolBar.add(filterSwitchButton); } private void buildTagToolBar() { styleToolBarMap = new HashMap<String, TagStyleToolBar>(); tagToolBar = new JToolBar(JToolBar.VERTICAL); tagToolBar.setFloatable(false); tagToolBar.setVisible(false); tagPageToolButton = new JToggleButton(IconUtils.getPageTagIcon()); tagPageToolButton.setToolTipText(_("Tag signal pages")); tagBlockToolButton = new JToggleButton(IconUtils.getBlockTagIcon()); tagBlockToolButton.setToolTipText(_("Tag signal blocks")); tagChannelToolButton = new JToggleButton(IconUtils.getChannelTagIcon()); tagChannelToolButton.setToolTipText(_("Tag single channel or multichannel custom size signal fragments")); tagToolBar.addSeparator(new Dimension(0,5)); tagToolBar.add(getEditTagAnnotationAction()); tagToolBar.add(getRemoveTagAction()); tagToolBar.addSeparator(new Dimension(0,5)); tagToolBar.add(tagPageToolButton); tagToolBar.add(tagBlockToolButton); tagToolBar.add(tagChannelToolButton); tagToolBar.addSeparator(new Dimension(0,5)); tagToolBarLayout = new CardLayout(); tagToolBarPanel = new JPanel(tagToolBarLayout); tagToolBarPanel.setAlignmentX(Component.LEFT_ALIGNMENT); tagToolBarPanel.add(new JPanel(), "none"); tagToolBar.add(tagToolBarPanel); } public void snapPageToView() { for (SignalPlot plot : plots) { plot.snapPageToView(); } deferredSnapToPage = false; } public void showTime(float itimeme) { JScrollPane scrollPane = scrollPanes.getFirst(); SignalPlot plot = plots.getFirst(); JViewport viewport = scrollPane.getViewport(); Point point = viewport.getViewPosition(); Point2D p2D = plot.toSignalSpace(point); Point2D.Float newP2D = new Point2D.Float(itimeme,(float) p2D.getY()); Point newP = plot.toPixelSpace(newP2D); Dimension viewportSize = viewport.getExtentSize(); Dimension plotSize = plot.getSize(); newP.x = Math.max(0, Math.min(plotSize.width - viewportSize.width, newP.x)); newP.y = Math.max(0, Math.min(plotSize.height - viewportSize.height, newP.y)); viewport.setViewPosition(newP); } public void showTimeCentered(float time) { JScrollPane scrollPane = scrollPanes.getFirst(); SignalPlot plot = plots.getFirst(); JViewport viewport = scrollPane.getViewport(); Point point = viewport.getViewPosition(); Point2D p2D = plot.toSignalSpace(point); Point2D.Float newP2D = new Point2D.Float(time,(float) p2D.getY()); Point newP = plot.toPixelSpace(newP2D); Dimension viewportSize = viewport.getExtentSize(); Dimension plotSize = plot.getSize(); newP.x -= viewportSize.width/2; newP.x = Math.max(0, Math.min(plotSize.width - viewportSize.width, newP.x)); newP.y = Math.max(0, Math.min(plotSize.height - viewportSize.height, newP.y)); viewport.setViewPosition(newP); } public void showTag(Tag tag) { JScrollPane scrollPane = scrollPanes.getFirst(); SignalPlot plot = plots.getFirst(); JViewport viewport = scrollPane.getViewport(); Dimension viewportSize = viewport.getExtentSize(); int startX = plot.timeToPixel(tag.getPosition()); int endX = plot.timeToPixel(tag.getPosition()+tag.getLength()); int optimalX = startX - (viewportSize.width - (endX-startX)) / 2; Point newP = null; SignalSelectionType type = tag.getStyle().getType(); if (type.isPage() || type.isBlock()) { Point point = viewport.getViewPosition(); newP = new Point(optimalX, point.y); } else { // channel int startY = plot.channelToPixel(tag.getChannel()); int endY = startY + plot.getPixelPerChannel(); int optimalY = startY - (viewportSize.height - (endY-startY)) / 2; newP = new Point(optimalX, optimalY); } Dimension plotSize = plot.getSize(); newP.x = Math.max(0, Math.min(plotSize.width - viewportSize.width, newP.x)); newP.y = Math.max(0, Math.min(plotSize.height - viewportSize.height, newP.y)); viewport.setViewPosition(newP); } public boolean isToolEngaged() { return ((null != currentSignalTool) && (currentSignalTool.isEngaged())); } @Override public SignalDocument getDocument() { return document; } public TagDifferenceDetector getTagDifferenceDetector() { if (tagDifferenceDetector == null) { tagDifferenceDetector = new TagDifferenceDetector(); } return tagDifferenceDetector; } public ApplicationConfiguration getApplicationConfig() { return applicationConfig; } public void setApplicationConfig(ApplicationConfiguration applicationConfig) { this.applicationConfig = applicationConfig; } public ActionFocusManager getActionFocusManager() { return actionFocusManager; } public void setActionFocusManager(ActionFocusManager actionFocusManager) { this.actionFocusManager = actionFocusManager; } public NewTagAction getNewTagAction() { if (newTagAction == null) { newTagAction = new NewTagAction(this); newTagAction.setDocumentFlowIntegrator(documentFlowIntegrator); newTagAction.setNewTagDialog(newTagDialog); } return newTagAction; } public OpenTagAction getOpenTagAction() { if (openTagAction == null) { openTagAction = new OpenTagAction(this); openTagAction.setDocumentFlowIntegrator(documentFlowIntegrator); openTagAction.setFileChooser(fileChooser); openTagAction.setOptionPaneParent(this); } return openTagAction; } public CloseTagAction getCloseTagAction() { if (closeTagAction == null) { closeTagAction = new CloseTagAction(this); closeTagAction.setDocumentFlowIntegrator(documentFlowIntegrator); } return closeTagAction; } public SaveTagAction getSaveTagAction() { if (saveTagAction == null) { saveTagAction = new SaveTagAction(this); saveTagAction.setDocumentFlowIntegrator(documentFlowIntegrator); } return saveTagAction; } public SaveTagAsAction getSaveTagAsAction() { if (saveTagAsAction == null) { saveTagAsAction = new SaveTagAsAction(this); saveTagAsAction.setDocumentFlowIntegrator(documentFlowIntegrator); } return saveTagAsAction; } public PreciseSelectionAction getPreciseSelectionAction() { if (preciseSelectionAction == null) { preciseSelectionAction = new PreciseSelectionAction(this); preciseSelectionAction.setSignalSelectionDialog(signalSelectionDialog); } return preciseSelectionAction; } public TagSelectionAction getTagSelectionAction() { if (tagSelectionAction == null) { tagSelectionAction = new TagSelectionAction(this); } return tagSelectionAction; } public RemoveTagAction getRemoveTagAction() { if (removeTagAction == null) { removeTagAction = new RemoveTagAction(this); } return removeTagAction; } /** * Returns an {@link Action} responsible for starting a new monitor * recording (it shows a dialog which allows to select recording target * files and starts the recording). * @return an {@link Action} responsible for starting a new monitor * recording */ public StartMonitorRecordingAction getStartMonitorRecordingAction() { if (startMonitorRecordingAction == null) { startMonitorRecordingAction = new StartMonitorRecordingAction(getActionFocusManager()); startMonitorRecordingAction.setStartMonitorRecordingDialog(startMonitorRecordingDialog); } return startMonitorRecordingAction; } /** * Returns an {@link Action} responsible for stopping a monitor recording. * @return an {@link Action} responsible for stopping an monitor * recording */ public StopMonitorRecordingAction getStopMonitorRecordingAction() { if (stopMonitorRecordingAction == null) { stopMonitorRecordingAction = new StopMonitorRecordingAction(getActionFocusManager()); } return stopMonitorRecordingAction; } public MonitorRecordingDurationPanel getMonitorRecordingDurationPanel() { if (monitorRecordingDurationPanel == null) { monitorRecordingDurationPanel = new MonitorRecordingDurationPanel(document); } return monitorRecordingDurationPanel; } public EditSignalParametersAction getEditSignalParametersAction() { if (editSignalParametersAction == null) { editSignalParametersAction = new EditSignalParametersAction(this); editSignalParametersAction.setSignalParametersDialog(signalParametersDialog); } return editSignalParametersAction; } public EditSignalMontageAction getEditSignalMontageAction() { if (editSignalMontageAction == null) { editSignalMontageAction = new EditSignalMontageAction(this); editSignalMontageAction.setSignalMontageDialog(signalMontageDialog); } return editSignalMontageAction; } public ApplyDefaultMontageAction getApplyDefaultMontageAction() { if (applyDefaultMontageAction == null) { applyDefaultMontageAction = new ApplyDefaultMontageAction(this); } return applyDefaultMontageAction; } public DisplayClockTimeAction getDisplayClockTimeAction() { if (displayClockTimeAction == null) { displayClockTimeAction = new DisplayClockTimeAction(this); } return displayClockTimeAction; } public EditTagAnnotationAction getEditTagAnnotationAction() { if (editTagAnnotationAction == null) { editTagAnnotationAction = new EditTagAnnotationAction(this); editTagAnnotationAction.setEditTagAnnotationDialog(editTagAnnotationDialog); KeyStroke ctrla = KeyStroke.getKeyStroke("ctrl A"); getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT).put(ctrla, "editTagAnnotationAction"); getActionMap().put("editTagAnnotationAction", editTagAnnotationAction); } return editTagAnnotationAction; } public SnapToPageAction getSnapToPageAction() { if (snapToPageAction == null) { snapToPageAction = new SnapToPageAction(this); } return snapToPageAction; } public SignalFilterSwitchAction getFilterSwitchAction() { if (signalFilterSwitchAction == null) { signalFilterSwitchAction = new SignalFilterSwitchAction(this); } return signalFilterSwitchAction; } public boolean isDisplayClockTime() { return displayClockTime; } public void setDisplayClockTime(boolean displayClockTime) { if (this.displayClockTime != displayClockTime) { this.displayClockTime = displayClockTime; for (SignalPlot plot : plots) { plot.revalidateAndRepaintAll(); } } } public boolean isSnapToPageMode() { return snapToPageMode; } public void setSnapToPageMode(boolean snapToPageMode) { if (this.snapToPageMode != snapToPageMode) { this.snapToPageMode = snapToPageMode; getSnapToPageAction().putValue(AbstractAction.SELECTED_KEY, new Boolean(snapToPageMode)); timeScaleSlider.setEnabled(!snapToPageMode); // zoomMouseWheelListener.setTimeEnabled(!snapToPageMode); if (snapToPageMode) { if (isVisible()) { snapPageToView(); } else { deferredSnapToPage = true; } } } } public ZoomSignalTool getZoomSignalTool() { return zoomSignalTool; } public DocumentFlowIntegrator getDocumentFlowIntegrator() { return documentFlowIntegrator; } public void setDocumentFlowIntegrator(DocumentFlowIntegrator documentFlowIntegrator) { this.documentFlowIntegrator = documentFlowIntegrator; } public MontagePresetManager getMontagePresetManager() { return montagePresetManager; } public void setMontagePresetManager(MontagePresetManager montagePresetManager) { if (this.montagePresetManager != montagePresetManager) { if (this.montagePresetManager != null) { this.montagePresetManager.removePresetManagerListener(montagePresetManagerListener); } this.montagePresetManager = montagePresetManager; if (montagePresetManager != null) { if (montagePresetManagerListener == null) { montagePresetManagerListener = new PresetManagerAdapter() { @Override public void defaultPresetChanged(PresetManagerEvent ev) { afSupport.fireActionFocusChanged(); } }; } montagePresetManager.addPresetManagerListener(montagePresetManagerListener); } } } public NewTagDialog getNewTagDialog() { return newTagDialog; } public void setNewTagDialog(NewTagDialog newTagDialog) { this.newTagDialog = newTagDialog; } public ViewerFileChooser getFileChooser() { return fileChooser; } public void setFileChooser(ViewerFileChooser fileChooser) { this.fileChooser = fileChooser; } public SignalSelectionDialog getSignalSelectionDialog() { return signalSelectionDialog; } public void setSignalSelectionDialog(SignalSelectionDialog signalSelectionDialog) { this.signalSelectionDialog = signalSelectionDialog; } public StartMonitorRecordingDialog getStartMonitorRecordingDialog() { return startMonitorRecordingDialog; } public void setStartMonitorRecordingDialog(StartMonitorRecordingDialog startMonitorRecordingDialog) { this.startMonitorRecordingDialog = startMonitorRecordingDialog; getStartMonitorRecordingAction().setStartMonitorRecordingDialog(startMonitorRecordingDialog); } public SignalParametersDialog getSignalParametersDialog() { return signalParametersDialog; } public void setSignalParametersDialog(SignalParametersDialog signalParametersDialog) { this.signalParametersDialog = signalParametersDialog; } public SignalMontageDialog getSignalMontageDialog() { return signalMontageDialog; } public void setSignalMontageDialog(SignalMontageDialog signalMontageDialog) { this.signalMontageDialog = signalMontageDialog; } public EditTagAnnotationDialog getEditTagAnnotationDialog() { return editTagAnnotationDialog; } public void setEditTagAnnotationDialog(EditTagAnnotationDialog editTagAnnotationDialog) { this.editTagAnnotationDialog = editTagAnnotationDialog; } public TagStylePaletteDialog getTagStylePaletteDialog() { return tagStylePaletteDialog; } public void setTagStylePaletteDialog(TagStylePaletteDialog tagStylePaletteDialog) { this.tagStylePaletteDialog = tagStylePaletteDialog; } public EditTagDescriptionDialog getEditTagDescriptionDialog() { return editTagDescriptionDialog; } public void setEditTagDescriptionDialog(EditTagDescriptionDialog editTagDescriptionDialog) { this.editTagDescriptionDialog = editTagDescriptionDialog; } public JScrollPane getMasterScrollPane() { return scrollPanes.getFirst(); } @Override public SignalPlot getMasterPlot() { return plots.getFirst(); } public LinkedList<SignalPlot> getPlots() { return plots; } public LinkedList<SignalPlotScrollPane> getScrollPanes() { return scrollPanes; } public SlavePlotSettingsPopupDialog getSlavePlotSettingsPopupDialog() { return slavePlotSettingsPopupDialog; } public void setSlavePlotSettingsPopupDialog(SlavePlotSettingsPopupDialog slavePlotSettingsPopupDialog) { this.slavePlotSettingsPopupDialog = slavePlotSettingsPopupDialog; } public ChannelOptionsPopupDialog getChannelOptionsPopupDialog() { return this.channelOptionsPopupDialog; } public void setChannelOptionsPopupDialog(ChannelOptionsPopupDialog channelOptionsPopupDialog) { this.channelOptionsPopupDialog = channelOptionsPopupDialog; } private SignalPlotOptionsPopupDialog getPlotOptionsDialog() { if (signalPlotOptionsPopupDialog == null) { signalPlotOptionsPopupDialog = new SignalPlotOptionsPopupDialog((Window) getTopLevelAncestor(), true); signalPlotOptionsPopupDialog.setSignalView(this); } return signalPlotOptionsPopupDialog; } private ZoomSettingsPopupDialog getZoomSettingsDialog() { if (zoomSettingsDialog == null) { zoomSettingsDialog = new ZoomSettingsPopupDialog((Window) getTopLevelAncestor(), true); } return zoomSettingsDialog; } @Override public void destroy() { document.removePropertyChangeListener(hypnogramPlot); document.removePropertyChangeListener(this); if (this.montagePresetManager != null) { this.montagePresetManager.removePresetManagerListener(montagePresetManagerListener); } for (SignalPlot plot : plots) { plot.destroy(); } closed = true; } public boolean isClosed() { return closed; } @Override public SignalSelectionType getCurrentTagType() { if ((currentSignalTool != null) && (currentSignalTool instanceof TaggingSignalTool)) { return ((TaggingSignalTool) currentSignalTool).getTagType(); } return null; } private TagStyleToolBar getTagStyleToolBar(SignalSelectionType type) { TagDocument activeTag = document.getActiveTag(); if (activeTag != null) { String hash = Integer.toHexString(activeTag.hashCode()); return styleToolBarMap.get(hash+"-"+type.getName()); } return null; } @Override public TagStyle getCurrentTagStyle(SignalSelectionType type) { if (type != null) { TagStyleToolBar toolBar = getTagStyleToolBar(type); if (toolBar != null) { return toolBar.getSelectedStyle(); } } return null; } public TagIconProducer getTagIconProducer() { return tagIconProducer; } public TagDocument[] getComparedTags() { return comparedTags; } public void setComparedTags(TagDocument tag1, TagDocument tag2) { if (tag1 == null || tag2 == null) { comparedTags = null; } else { if (!document.getTagDocuments().contains(tag1) || !document.getTagDocuments().contains(tag2)) { throw new SanityCheckException("Tag to compare not in presented document"); } comparedTags = new TagDocument[] { tag1, tag2 }; TagDocument activeTag = document.getActiveTag(); if (tag1 != activeTag && tag2 != activeTag) { document.setActiveTag(tag1); } } clearTagSelection(); for (SignalPlotColumnHeader columnHeader : columnHeaders) { columnHeader.reset(); columnHeader.revalidate(); } onTagsChanged(); } public void updateTagComparison() { if (comparedTags == null) { differenceSet = null; return; } TagDifferenceDetector detector = getTagDifferenceDetector(); SortedSet<Tag> tags1 = comparedTags[0].getTagSet().getTags(); SortedSet<Tag> tags2 = comparedTags[1].getTagSet().getTags(); TreeSet<TagDifference> differences = new TreeSet<TagDifference>(); detector.getDifferences(tags1, tags2, SignalSelectionType.PAGE, SignalSelection.CHANNEL_NULL, differences); detector.getDifferences(tags1, tags2, SignalSelectionType.BLOCK, SignalSelection.CHANNEL_NULL, differences); detector.getDifferences(tags1, tags2, SignalSelectionType.CHANNEL, SignalSelection.CHANNEL_NULL, differences); int cnt = document.getChannelCount(); for (int i=0; i<cnt; i++) { detector.getDifferences(tags1, tags2, SignalSelectionType.CHANNEL, i, differences); } differenceSet = new TagDifferenceSet(differences); } public boolean isComparingTags() { return(comparedTags != null); } public TagDifferenceSet getDifferenceSet() { return differenceSet; } @Override public void propertyChange(PropertyChangeEvent evt) { Object source = evt.getSource(); String name = evt.getPropertyName(); if (source == document) { if (name.equals(SignalDocument.TAG_DOCUMENTS_PROPERTY)) { TagDocument newDocument = (TagDocument) evt.getNewValue(); TagDocument oldDocument = (TagDocument) evt.getOldValue(); if (newDocument != null) { TagStyleToolBar pageToolBar = new TagStyleToolBar(newDocument.getTagSet(), SignalSelectionType.PAGE, tagIconProducer, getTagSelectionAction()); TagStyleToolBar blockToolBar = new TagStyleToolBar(newDocument.getTagSet(), SignalSelectionType.BLOCK, tagIconProducer, getTagSelectionAction()); TagStyleToolBar channelToolBar = new TagStyleToolBar(newDocument.getTagSet(), SignalSelectionType.CHANNEL, tagIconProducer, getTagSelectionAction()); String hash = Integer.toHexString(newDocument.hashCode()); tagToolBarPanel.add(pageToolBar, hash+"-page"); tagToolBarPanel.add(blockToolBar, hash+"-block"); tagToolBarPanel.add(channelToolBar, hash+"-channel"); styleToolBarMap.put(hash+"-page", pageToolBar); styleToolBarMap.put(hash+"-block", blockToolBar); styleToolBarMap.put(hash+"-channel", channelToolBar); StyledTagSet tagSet = newDocument.getTagSet(); tagSet.addTagListener(this); tagSet.addTagStyleListener(this); tagSet.addTagListener(hypnogramPlot); newDocument.addPropertyChangeListener(this); } if (oldDocument != null) { String hash = Integer.toHexString(oldDocument.hashCode()); TagStyleToolBar pageToolBar = styleToolBarMap.get(hash+"-page"); TagStyleToolBar blockToolBar = styleToolBarMap.get(hash+"-block"); TagStyleToolBar channelToolBar = styleToolBarMap.get(hash+"-channel"); if (pageToolBar != null) { tagToolBar.remove(pageToolBar); styleToolBarMap.remove(hash+"-page"); } if (blockToolBar != null) { tagToolBar.remove(blockToolBar); styleToolBarMap.remove(hash+"-block"); } if (channelToolBar != null) { tagToolBar.remove(channelToolBar); styleToolBarMap.remove(hash+"-channel"); } StyledTagSet tagSet = oldDocument.getTagSet(); tagSet.removeTagListener(this); tagSet.removeTagStyleListener(this); tagSet.removeTagListener(hypnogramPlot); oldDocument.removePropertyChangeListener(this); if (isComparingTags()) { if (oldDocument == comparedTags[0] || oldDocument == comparedTags[1]) { setComparedTags(null,null); } } } int tagCnt = document.getTagDocuments().size(); if (tagCnt > 1) { for (SignalPlotScrollPane scrollPane : scrollPanes) { scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE); } } else { for (SignalPlotScrollPane scrollPane : scrollPanes) { scrollPane.getViewport().setScrollMode(JViewport.BLIT_SCROLL_MODE); } } hypnogramPlot.revalidateAndReset(); } else if (name.equals(SignalDocument.ACTIVE_TAG_PROPERTY)) { TagDocument newDocument = (TagDocument) evt.getNewValue(); if (newDocument != null) { updateTagStylePanel(newDocument); tagToolBar.setVisible(true); } else { tagToolBar.setVisible(false); } if (signalSelection != null && !signalSelection.getType().isChannel()) { // clear page or block signal selection because page/block parameters may have changed clearSignalSelection(); } clearTagSelection(); rebindTagKeys(); afSupport.fireActionFocusChanged(); // resnap to page for new tag if (snapToPageMode) { SwingUtilities.invokeLater(new ResnapToPageRunnable(this)); } } else if (name.equals(SignalDocument.MONTAGE_PROPERTY)) { getFilterSwitchAction().putValue(AbstractAction.SELECTED_KEY, new Boolean(((Montage) evt.getNewValue()).isFilteringEnabled())); } } else if (source instanceof TagDocument) { TagDocument tagDocument = (TagDocument) source; if (!document.getTagDocuments().contains(tagDocument)) { logger.warn("Stray tag document change event?"); return; } if (TagDocument.SAVED_PROPERTY.equals(name)) { if (tagDocument == document.getActiveTag()) { afSupport.fireActionFocusChanged(); } } } } private void updateTagStylePanel(TagDocument tagDocument) { String hash = Integer.toHexString(tagDocument.hashCode()); ButtonModel model = toolButtonGroup.getSelection(); if (model != null) { SignalTool tool = toolMap.get(model); String key = null; boolean tagSelection = false; if (tool != null) { if (tool == tagPageSignalTool || tool == selectPageTool) { key = hash+"-page"; } else if (tool == tagBlockSignalTool || tool == selectBlockTool) { key = hash+"-block"; } else if (tool == tagChannelSignalTool || tool == selectChannelTool) { key = hash+"-channel"; } else { key = "none"; } tagSelection = (tool instanceof SelectionSignalTool); TagStyleToolBar toolBar = styleToolBarMap.get(key); if (toolBar != null) { toolBar.setTagSelectionOnButtonClick(tagSelection); if (tagSelection) { toolBar.clearSelection(); } else { toolBar.assertAnySelection(); } } tagToolBarLayout.show(tagToolBarPanel, key); } } } private void onTagsChanged() { // TODO consider either incremental update or incremental comparison (compare only visible areas) updateTagComparison(); for (SignalPlot plot : plots) { plot.repaint(); } for (SignalPlotColumnHeader columnHeader : columnHeaders) { columnHeader.repaint(); } } private void onTagStylesChanged() { updateTagComparison(); for (SignalPlot plot : plots) { plot.repaint(); } for (SignalPlotColumnHeader columnHeader : columnHeaders) { columnHeader.repaint(); } rebindTagKeys(); } @Override public void tagAdded(TagEvent e) { onTagsChanged(); } @Override public void tagChanged(TagEvent e) { onTagsChanged(); } @Override public void tagRemoved(TagEvent e) { PositionedTag selectedTag = getTagSelection(); if (selectedTag != null && selectedTag.tag == e.getTag()) { clearTagSelection(); } onTagsChanged(); } @Override public void tagStyleAdded(TagStyleEvent e) { onTagStylesChanged(); } @Override public void tagStyleChanged(TagStyleEvent e) { onTagStylesChanged(); onTagsChanged(); } @Override public void tagStyleRemoved(TagStyleEvent e) { onTagStylesChanged(); onTagsChanged(); } private void rebindTagKeys() { InputMap inputMap = getInputMap(JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT); ActionMap actionMap = getActionMap(); // clear previous bindings if (lastStylesByKeyStrokes != null) { Set<KeyStroke> keySet = lastStylesByKeyStrokes.keySet(); for (KeyStroke keyStroke : keySet) { actionMap.remove("keyTagPressed-" + keyStroke.hashCode()); inputMap.remove(keyStroke); } lastStylesByKeyStrokes = null; } TagDocument activeTag = document.getActiveTag(); if (activeTag != null) { StyledTagSet tagSet = activeTag.getTagSet(); HashMap<KeyStroke,TagStyle> stylesByKeyStrokes = tagSet.getStylesByKeyStrokes(); Set<Entry<KeyStroke,TagStyle>> entrySet = stylesByKeyStrokes.entrySet(); KeyStroke keyStroke; String actionName; for (Entry<KeyStroke,TagStyle> entry : entrySet) { keyStroke = entry.getKey(); actionName = "keyTagPressed-" + keyStroke.hashCode(); inputMap.put(keyStroke, actionName); actionMap.put(actionName, new TagKeyRedirectAction(entry.getValue())); } lastStylesByKeyStrokes = stylesByKeyStrokes; } } private void setCurrentSignalTool(SignalTool tool) { currentSignalTool = tool; for (SignalPlot plot : plots) { plot.setCursor(currentSignalTool.getDefaultCursor()); } if (currentSignalTool.supportsColumnHeader()) { for (SignalPlotColumnHeader columnHeader : columnHeaders) { columnHeader.setCursor(currentSignalTool.getDefaultCursor()); } } else { for (SignalPlotColumnHeader columnHeader : columnHeaders) { columnHeader.setCursor(Cursor.getDefaultCursor()); } } if (currentSignalTool.supportsRowHeader()) { for (SignalPlotRowHeader rowHeader : rowHeaders) { rowHeader.setCursor(currentSignalTool.getDefaultCursor()); } } else { for (SignalPlotRowHeader rowHeader : rowHeaders) { rowHeader.setCursor(Cursor.getDefaultCursor()); } } TagDocument activeTag = document.getActiveTag(); if (activeTag != null) { updateTagStylePanel(activeTag); } else { tagToolBarLayout.show(tagToolBarPanel, "none"); } if (tagSelection != null) { if (currentSignalTool instanceof SelectionSignalTool) { clearTagSelection(); } else if (currentSignalTool instanceof TaggingSignalTool) { SignalSelectionType type = tagSelection.tag.getType(); if (type != ((TaggingSignalTool) currentSignalTool).getTagType()) { clearTagSelection(); } } } if (signalSelection != null) { if (currentSignalTool instanceof TaggingSignalTool) { clearSignalSelection(); } else if (currentSignalTool instanceof SelectionSignalTool) { SignalSelectionType type = signalSelection.getType(); if (type != ((SelectionSignalTool) currentSignalTool).getSelectionType()) { clearSignalSelection(); } } } toolMouseAdapter.setSignalTool(currentSignalTool); columnToolMouseAdapter.setSignalTool(currentSignalTool); rowToolMouseAdapter.setSignalTool(currentSignalTool); } public SignalSpaceConstraints createSignalSpaceConstraints() { SignalSpaceConstraints constraints = new SignalSpaceConstraints(); constraints.setTagIconProducer(getTagIconProducer()); SignalPlot masterPlot = getMasterPlot(); MultichannelSampleSource montage = masterPlot.getSignalOutput(); MultichannelSampleSource source = masterPlot.getSignalSource(); int channelCount = source.getChannelCount(); String[] labels = new String[channelCount]; int i; for (i=0; i<channelCount; i++) { labels[i] = source.getLabel(i); } constraints.setSourceChannels(labels); channelCount = montage.getChannelCount(); labels = new String[channelCount]; for (i=0; i<channelCount; i++) { labels[i] = montage.getLabel(i); } constraints.setChannels(labels); constraints.setSignalLength(masterPlot.getMaxSampleCount()); constraints.setTimeSignalLength(masterPlot.getMaxTime()); constraints.setSamplingFrequency(masterPlot.getSamplingFrequency()); constraints.setPageSize(masterPlot.getPageSize()); constraints.setBlocksPerPage(masterPlot.getBlocksPerPage()); constraints.setBlockSize(masterPlot.getBlockSize()); constraints.setMaxBlock(masterPlot.getBlockCount()-1); constraints.setMaxPage(masterPlot.getPageCount()-1); constraints.setMaxWholePage(masterPlot.getWholePageCount()-1); return constraints; } private class PlotOptionsButtonListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { SignalPlotOptionsPopupDialog dialog = getPlotOptionsDialog(); dialog.initializeNow(); Container ancestor = getTopLevelAncestor(); Point containerLocation = ancestor.getLocation(); Point location = SwingUtilities.convertPoint(plotOptionsButton, new Point(0,0), ancestor); location.translate(containerLocation.x-(dialog.getSize().width-plotOptionsButton.getSize().width)/2, containerLocation.y); dialog.setLocation(location); dialog.showDialog(null); } } private class ZoomSignalToolButtonMouseListener extends MouseAdapter { private Timer timer; ActionListener timerListener = new ActionListener() { @Override public void actionPerformed(ActionEvent e) { ZoomSettingsPopupDialog dialog = getZoomSettingsDialog(); Container ancestor = getTopLevelAncestor(); Point containerLocation = ancestor.getLocation(); Point location = SwingUtilities.convertPoint(zoomSignalToolButton, new Point(0,0), ancestor); location.translate(containerLocation.x, containerLocation.y); dialog.setLocation(location); zoomSignalToolButton.doClick(); dialog.showDialog(zoomSignalTool); } }; @Override public void mousePressed(MouseEvent e) { if (timer == null) { timer = new Timer(400, timerListener); // popup after 400 ms timer.setRepeats(false); } timer.start(); } @Override public void mouseReleased(MouseEvent e) { if (timer != null) { timer.stop(); } } @Override public void mouseExited(MouseEvent e) { if (timer != null) { timer.stop(); } } } /* (non-Javadoc) * @see org.signalml.plugin.export.view.ExportedSignalView#setSignalSelection(org.signalml.plugin.export.view.ExportedSignalPlot, org.signalml.plugin.export.signal.ExportedSignalSelection) */ @Override public void setSignalSelection(ExportedSignalPlot plot, ExportedSignalSelection signalSelection) throws InvalidClassException { if (plot instanceof SignalPlot) setSignalSelection((SignalPlot) plot, new SignalSelection(signalSelection)); else throw new InvalidClassException("only plot got from Svarog can be used"); } /* (non-Javadoc) * @see org.signalml.plugin.export.view.ExportedSignalView#setTagSelection(org.signalml.plugin.export.view.ExportedSignalPlot, org.signalml.plugin.export.view.ExportedPositionedTag) */ @Override public void setTagSelection(ExportedSignalPlot plot, ExportedPositionedTag tagSelection) throws InvalidClassException { if (plot instanceof SignalPlot) { SignalPlot signalPlot = (SignalPlot) plot; setTagSelection(signalPlot, new PositionedTag(tagSelection)); } else throw new InvalidClassException("only plot got from Svarog can be used"); } private class ToolSelectionListener implements ActionListener { @Override public void actionPerformed(ActionEvent e) { SignalTool signalTool = toolMap.get(toolButtonGroup.getSelection()); if (signalTool != null) { setCurrentSignalTool(signalTool); } else { logger.warn("WARNING: unbound signal tool"); } } } protected class DelKeyRedirectAction extends AbstractAction implements TagStyleSelector { private static final long serialVersionUID = 1L; @Override public TagStyle getTagStyle() { return null; } @Override public void actionPerformed(ActionEvent e) { if (currentSignalTool instanceof SelectionSignalTool) { getTagSelectionAction().actionPerformed(new ActionEvent(this, 0, "delete")); } else { getRemoveTagAction().actionPerformed(e); } } @Override public boolean isEnabled() { if (currentSignalTool instanceof SelectionSignalTool) { return getTagSelectionAction().isEnabled(); } else { return getRemoveTagAction().isEnabled(); } } } protected class TagKeyRedirectAction extends AbstractAction implements TagStyleSelector { private static final long serialVersionUID = 1L; private TagStyle tagStyle; public TagKeyRedirectAction(TagStyle tagStyle) { if (tagStyle == null) { throw new NullPointerException("No style"); } this.tagStyle = tagStyle; } @Override public TagStyle getTagStyle() { return tagStyle; } @Override public void actionPerformed(ActionEvent e) { if (currentSignalTool instanceof SelectionSignalTool) { if (signalSelection == null) { return; } SignalSelectionType currentTagType = signalSelection.getType(); SignalSelectionType desiredTagType = tagStyle.getType(); if (currentTagType != desiredTagType) { // ignore bindings to other style types return; } getTagSelectionAction().actionPerformed(new ActionEvent(this, 0, "tag")); } else if (currentSignalTool instanceof TaggingSignalTool) { SignalSelectionType currentTagType = getCurrentTagType(); SignalSelectionType desiredTagType = tagStyle.getType(); if (currentTagType != desiredTagType) { // change selected style type if (desiredTagType.isPage()) { tagPageToolButton.doClick(); } else if (desiredTagType.isBlock()) { tagBlockToolButton.doClick(); } else if (desiredTagType.isChannel()) { tagChannelToolButton.doClick(); } else { throw new SanityCheckException("Bad selection type"); } } TagStyleToolBar toolBar = getTagStyleToolBar(desiredTagType); if (toolBar == null) { return; } toolBar.setSelectedStyle(tagStyle); } } @Override public boolean isEnabled() { if (currentSignalTool instanceof SelectionSignalTool) { return getTagSelectionAction().isEnabled(); } else if (currentSignalTool instanceof TaggingSignalTool) { return true; } return false; } } }