/* * DataPanel.java * * Created on Aug 28, 2009, 9:06:25 AM */ package org.autoplot; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.util.logging.Logger; import javax.swing.SwingUtilities; import org.autoplot.help.AutoplotHelpSystem; import org.jdesktop.beansbinding.AutoBinding.UpdateStrategy; import org.jdesktop.beansbinding.BeanProperty; import org.jdesktop.beansbinding.BindingGroup; import org.jdesktop.beansbinding.Bindings; import org.autoplot.dom.Application; import org.autoplot.dom.ApplicationController; import org.autoplot.dom.DataSourceController; import org.autoplot.dom.DataSourceFilter; import org.autoplot.dom.PlotElement; import org.autoplot.dom.PlotElementController; import org.das2.qds.DataSetUtil; import org.das2.qds.QDataSet; /** * GUI tab for controlling how data is handled, provides feedback for what is * being plotted, allows fill value to be specified, and provides the filters * chain of operations to apply to the data before plotting. * @author jbf */ public class DataPanel extends javax.swing.JPanel { private final Application dom; private final AutoplotUI app; private final ApplicationController applicationController; private DataSourceFilter dsf; // current focus private BindingGroup dataSourceFilterBindingGroup; private boolean dataSourceFilterBindingGroupIsBound= false; private BindingGroup plotElementBindingGroup; private boolean plotElementBindingGroupIsBound= false; private PlotElement plotElement;// current focus private final static Logger logger = org.das2.util.LoggerManager.getLogger("autoplot.gui.data"); public DataPanel( AutoplotUI app) { initComponents(); plotElementFiltersPanel.setName("postOperationsPanel"); dataSourceFiltersPanel.setName("operationsPanel"); this.app= app; this.dom = app.getDom(); this.applicationController= this.dom.getController(); this.applicationController.addPropertyChangeListener( ApplicationController.PROP_PLOT_ELEMENT, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Runnable run= new Runnable() { @Override public void run() { doPlotElementBindings(); } }; if ( SwingUtilities.isEventDispatchThread() ) { run.run(); } else { SwingUtilities.invokeLater(run); } } }); this.applicationController.addPropertyChangeListener(ApplicationController.PROP_DATASOURCEFILTER, new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { Runnable run= new Runnable() { @Override public void run() { doDataSourceFilterBindings(); } }; if ( SwingUtilities.isEventDispatchThread() ) { run.run(); } else { SwingUtilities.invokeLater(run); } } }); AutoplotHelpSystem.getHelpSystem().registerHelpID(this.jPanel1, "dataPanel_1"); AutoplotHelpSystem.getHelpSystem().registerHelpID(this.jPanel2, "dataPanel_2"); plotElementFiltersPanel.setFilter(""); doBindings(); dataSetSelector.setPlayButton(false); dataSourceFiltersPanel.setVisible(false); jLabel2.setVisible(false); } protected void setExpertMode( boolean expert ) { plotElementFiltersPanel.setExpertMode(expert); dataSourceFiltersPanel.setExpertMode(expert); dataSetSelector.setEnabled(expert); dataSourceFiltersPanel.setEnabled(expert); additionalOperationsCheckBox.setEnabled(expert); doSuppressReset.setEnabled(expert); } /** * encourage making local copies for thread safety. * @return the current plotElement. */ private PlotElement getElement() { return plotElement; } /** * to avoid use of synchronized blocks, methods must be called from the * event thread. This verifies that the thread is the event thread. * @param caller */ private static void assertEventThread( String caller ) { if ( !SwingUtilities.isEventDispatchThread() ) { throw new IllegalArgumentException( caller + " must be called from the event thread."); } } /** * bind to the data source and plot plotElement. * This must be called on the event thread. */ protected final void doBindings() { logger.fine("doBindings"); doPlotElementBindings(); doDataSourceFilterBindings(); } @Override public void paint(Graphics g) { if ( !dataSourceFilterBindingGroupIsBound ) { dataSourceFilterBindingGroup.bind(); dataSourceFilterBindingGroupIsBound= true; } if ( !plotElementBindingGroupIsBound ) { plotElementBindingGroup.bind(); plotElementBindingGroupIsBound= true; } super.paint(g); //To change body of generated methods, choose Tools | Templates. } protected boolean adjusting = false; /** * true indicates the component is in transition. */ public static final String PROP_ADJUSTING = "adjusting"; public boolean isAdjusting() { return adjusting; } public void setAdjusting(boolean adjusting) { boolean oldAdjusting = this.adjusting; this.adjusting = adjusting; firePropertyChange(PROP_ADJUSTING, oldAdjusting, adjusting); } /** * show the context after the slicing and operations for the user's reference. * TODO: make sure this is released so stray datasets can be garbage collected. */ private final transient PropertyChangeListener contextListener= new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { updateProcessDataSetLabel(); } }; /** * if there are filters, then make sure the dialog is shown. */ private final transient PropertyChangeListener filtersListener= new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { if ( dsf.getFilters().trim().length()>0 ) { DataPanel.this.additionalOperationsCheckBox.setSelected(true); } } }; private final transient PropertyChangeListener fillDataSetListener= new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { final QDataSet ds= (QDataSet)evt.getNewValue(); Runnable run= new Runnable() { @Override public void run() { plotElementFiltersPanel.setDataSet(ds); } }; if ( SwingUtilities.isEventDispatchThread() ) { run.run(); } else { SwingUtilities.invokeLater(run); } } }; private void updateProcessDataSetLabel() { PlotElement lelement= getElement(); if ( lelement!=null ) { DataSourceFilter dsf1= dom.getController().getDataSourceFilterFor(lelement); QDataSet orig= dsf1==null ? null : dsf1.getController().getFillDataSet(); QDataSet proc= lelement.getController().getDataSet(); if ( orig==proc || proc==null ) { processDataSetLabel.setText( "" ); } else { String lbl= String.valueOf( proc ); QDataSet ds= lelement.getController().getDataSet(); String s= DataSetUtil.contextAsString( ds ).trim(); processDataSetLabel.setText( "These operations result in the dataset
"+lbl + ( s.length()==0 ? "" : ( "
@ "+ s ) ) ); } } else { processDataSetLabel.setText( "" ); } } private void doPlotElementBindings() { assertEventThread("doPlotElementBindings"); BindingGroup bc = new BindingGroup(); if (plotElementBindingGroup != null) plotElementBindingGroup.unbind(); setAdjusting( true ); // suppress events if ( plotElement!=null ) { plotElement.getController().removePropertyChangeListener( PlotElementController.PROP_DATASET, contextListener ); } PlotElement p = applicationController.getPlotElement(); plotElement= p; if ( p!=null ) { plotElementFiltersPanel.setFilter(p.getComponent()); } else { plotElementFiltersPanel.setFilter(""); } Runnable run= new Runnable() { @Override public void run() { if ( plotElement!=null ) { plotElementFiltersPanel.setFilter(plotElement.getComponent()); // because adjusting==true. } else { plotElementFiltersPanel.setFilter(""); // because adjusting==true. } } }; if ( SwingUtilities.isEventDispatchThread() ) { run.run(); } else { SwingUtilities.invokeLater(run); } if ( plotElement!=null ) { bc.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, plotElement, BeanProperty.create("component"), this.plotElementFiltersPanel, BeanProperty.create( OperationsPanel.PROP_FILTER ) ) ); bc.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, plotElement.getController(), BeanProperty.create("sliceAutoranges"), this.sliceAutorangesCB, BeanProperty.create("selected") ) ); } plotElementBindingGroup = bc; plotElementBindingGroupIsBound= false; setAdjusting( false ); if ( p!=null ) { p.getController().addPropertyChangeListener( PlotElementController.PROP_DATASET, contextListener ); } updateProcessDataSetLabel(); repaint(); } /** * this should be called on the event thread. */ private void doDataSourceFilterBindings() { assertEventThread("doDataSourceFilterBindings"); if (dataSourceFilterBindingGroup != null) dataSourceFilterBindingGroup.unbind(); if ( dsf!=null ) { dsf.getController().removePropertyChangeListener(DataSourceController.PROP_FILLDATASET, fillDataSetListener ); dsf.removePropertyChangeListener( DataSourceFilter.PROP_FILTERS, filtersListener ); } final DataSourceFilter newDsf = applicationController.getDataSourceFilter(); if (newDsf == null) { dataSourceFilterBindingGroup = null; //dataSetLabel.setText( "(no dataset)" ); return; } final QDataSet ds= newDsf.getController().getFillDataSet(); //dataSetLabel.setText( ds==null ? "(no dataset)" : ds.toString() ); newDsf.getController().addPropertyChangeListener( DataSourceController.PROP_FILLDATASET, fillDataSetListener ); newDsf.addPropertyChangeListener( DataSourceFilter.PROP_FILTERS, filtersListener ); additionalOperationsCheckBox.setSelected( newDsf.getFilters().trim().length()>0 ); dataSourceFiltersPanel.setFilter( newDsf.getFilters() ); dataSourceFiltersPanel.setDataSet( newDsf.getController().getDataSet() ); plotElementFiltersPanel.setDataSet(ds); BindingGroup bc = new BindingGroup(); bc.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, newDsf, BeanProperty.create("filters"), this.dataSourceFiltersPanel, BeanProperty.create("filter")) ); bc.addBinding(Bindings.createAutoBinding( UpdateStrategy.READ_WRITE, newDsf, BeanProperty.create("uri"), this.dataSetSelector, BeanProperty.create("value"))); bc.addBinding(Bindings.createAutoBinding(UpdateStrategy.READ_WRITE, newDsf, BeanProperty.create("controller.dataSet"), this.dataSourceFiltersPanel, BeanProperty.create("dataSet"))); dataSourceFilterBindingGroup = bc; dataSourceFilterBindingGroupIsBound= false; dsf= newDsf; repaint(); } /** This method is called from within the constructor to * initialize the form. * WARNING: Do NOT modify this code. The content of this method is * always regenerated by the Form Editor. */ @SuppressWarnings("unchecked") // //GEN-BEGIN:initComponents private void initComponents() { bindingGroup = new org.jdesktop.beansbinding.BindingGroup(); jSplitPane1 = new javax.swing.JSplitPane(); jPanel1 = new javax.swing.JPanel(); jLabel2 = new javax.swing.JLabel(); dataSetSelector = new org.autoplot.datasource.DataSetSelector(); dataSourceFiltersPanel = new org.autoplot.OperationsPanel(); additionalOperationsCheckBox = new javax.swing.JCheckBox(); doSuppressReset = new javax.swing.JCheckBox(); jPanel2 = new javax.swing.JPanel(); sliceAutorangesCB = new javax.swing.JCheckBox(); jLabel1 = new javax.swing.JLabel(); processDataSetLabel = new javax.swing.JLabel(); plotElementFiltersPanel = new org.autoplot.OperationsPanel(); setName("dataPanel"); // NOI18N jSplitPane1.setDividerLocation(110); jSplitPane1.setOrientation(javax.swing.JSplitPane.VERTICAL_SPLIT); jPanel1.setBorder(javax.swing.BorderFactory.createTitledBorder("Data Source [?]")); jPanel1.setName("dataSourcePanel"); // NOI18N jLabel2.setFont(jLabel2.getFont().deriveFont(jLabel2.getFont().getSize()-4f)); jLabel2.setText("Apply these operations to the data after loading. Fill and valid range can be specified with putProperty filter."); org.jdesktop.beansbinding.Binding binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, additionalOperationsCheckBox, org.jdesktop.beansbinding.ELProperty.create("${selected}"), jLabel2, org.jdesktop.beansbinding.BeanProperty.create("enabled")); bindingGroup.addBinding(binding); dataSetSelector.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { dataSetSelectorActionPerformed(evt); } }); binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, additionalOperationsCheckBox, org.jdesktop.beansbinding.ELProperty.create("${selected}"), dataSourceFiltersPanel, org.jdesktop.beansbinding.BeanProperty.create("enabled")); bindingGroup.addBinding(binding); additionalOperationsCheckBox.setSelected(false); additionalOperationsCheckBox.setText("Apply additional operations immediately after data is loaded"); binding = org.jdesktop.beansbinding.Bindings.createAutoBinding(org.jdesktop.beansbinding.AutoBinding.UpdateStrategy.READ_WRITE, dataSourceFiltersPanel, org.jdesktop.beansbinding.ELProperty.create("${visible}"), additionalOperationsCheckBox, org.jdesktop.beansbinding.BeanProperty.create("selected")); bindingGroup.addBinding(binding); additionalOperationsCheckBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { additionalOperationsCheckBoxActionPerformed(evt); } }); doSuppressReset.setSelected(true); doSuppressReset.setText("Suppress Reset"); doSuppressReset.setToolTipText("Normally the play button would reset the axis ranges and plot style, but this suppresses this behavior."); org.jdesktop.layout.GroupLayout jPanel1Layout = new org.jdesktop.layout.GroupLayout(jPanel1); jPanel1.setLayout(jPanel1Layout); jPanel1Layout.setHorizontalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jPanel1Layout.createSequentialGroup() .addContainerGap() .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(dataSetSelector, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 578, Short.MAX_VALUE) .add(jPanel1Layout.createSequentialGroup() .add(additionalOperationsCheckBox) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(doSuppressReset)) .add(jPanel1Layout.createSequentialGroup() .add(12, 12, 12) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jLabel2, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(dataSourceFiltersPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)))) .addContainerGap()) ); jPanel1Layout.setVerticalGroup( jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jPanel1Layout.createSequentialGroup() .add(dataSetSelector, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(jPanel1Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.BASELINE) .add(additionalOperationsCheckBox) .add(doSuppressReset)) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(jLabel2) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(dataSourceFiltersPanel, org.jdesktop.layout.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE) .addContainerGap()) ); jSplitPane1.setTopComponent(jPanel1); jPanel1.getAccessibleContext().setAccessibleName("Data Source and Initial Processing [?]"); jPanel2.setBorder(javax.swing.BorderFactory.createTitledBorder("Data Post Processing [?]\n")); jPanel2.setName("dataPostProcessingPanel"); // NOI18N sliceAutorangesCB.setText("Autorange after operations"); sliceAutorangesCB.setToolTipText("Changing the slice index will re-autorange the data"); sliceAutorangesCB.setName("sliceAutorangesCB"); // NOI18N jLabel1.setFont(jLabel1.getFont().deriveFont(jLabel1.getFont().getSize()-4f)); jLabel1.setText("Apply additional operations to the dataset before plotting"); jLabel1.setName("DataPostProcessingInstructionsLabel"); // NOI18N processDataSetLabel.setFont(processDataSetLabel.getFont().deriveFont(processDataSetLabel.getFont().getSize()-4f)); processDataSetLabel.setText("(filtered dataset will go here)"); org.jdesktop.layout.GroupLayout jPanel2Layout = new org.jdesktop.layout.GroupLayout(jPanel2); jPanel2.setLayout(jPanel2Layout); jPanel2Layout.setHorizontalGroup( jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jPanel2Layout.createSequentialGroup() .addContainerGap() .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jLabel1, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(sliceAutorangesCB, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(jPanel2Layout.createSequentialGroup() .add(jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(plotElementFiltersPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .add(org.jdesktop.layout.GroupLayout.TRAILING, processDataSetLabel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) .addContainerGap()))) ); jPanel2Layout.setVerticalGroup( jPanel2Layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(jPanel2Layout.createSequentialGroup() .add(jLabel1) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(plotElementFiltersPanel, org.jdesktop.layout.GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE) .addPreferredGap(org.jdesktop.layout.LayoutStyle.UNRELATED) .add(sliceAutorangesCB) .addPreferredGap(org.jdesktop.layout.LayoutStyle.RELATED) .add(processDataSetLabel) .add(16, 16, 16)) ); jSplitPane1.setRightComponent(jPanel2); org.jdesktop.layout.GroupLayout layout = new org.jdesktop.layout.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(org.jdesktop.layout.GroupLayout.TRAILING, layout.createSequentialGroup() .add(jSplitPane1) .add(0, 0, 0)) ); layout.setVerticalGroup( layout.createParallelGroup(org.jdesktop.layout.GroupLayout.LEADING) .add(org.jdesktop.layout.GroupLayout.TRAILING, jSplitPane1) ); bindingGroup.bind(); }// //GEN-END:initComponents private void dataSetSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_dataSetSelectorActionPerformed final String uri= dataSetSelector.getValue(); int modifiers= evt.getModifiers(); boolean suppressReset= doSuppressReset.isSelected(); if ( suppressReset ) { Runnable run= new Runnable() { public void run() { app.dom.getController().plotUri( uri, false ); } }; new Thread( run, "plotUri" ).start(); } else { app.doPlotGoButton( uri, modifiers ); } }//GEN-LAST:event_dataSetSelectorActionPerformed private void additionalOperationsCheckBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_additionalOperationsCheckBoxActionPerformed dataSourceFiltersPanel.setVisible( additionalOperationsCheckBox.isSelected() ); jLabel2.setVisible( additionalOperationsCheckBox.isSelected() ); if ( !additionalOperationsCheckBox.isSelected() ) { dsf.setFilters(""); } }//GEN-LAST:event_additionalOperationsCheckBoxActionPerformed // /** // * for testing, provide access. // * @return the FiltersChainPanel // */ // public FiltersChainPanel getFiltersChainPanel() { // return plotElementFiltersPanel.getFiltersChainPanel(); // } // Variables declaration - do not modify//GEN-BEGIN:variables private javax.swing.JCheckBox additionalOperationsCheckBox; private org.autoplot.datasource.DataSetSelector dataSetSelector; private org.autoplot.OperationsPanel dataSourceFiltersPanel; private javax.swing.JCheckBox doSuppressReset; private javax.swing.JLabel jLabel1; private javax.swing.JLabel jLabel2; private javax.swing.JPanel jPanel1; private javax.swing.JPanel jPanel2; private javax.swing.JSplitPane jSplitPane1; private org.autoplot.OperationsPanel plotElementFiltersPanel; private javax.swing.JLabel processDataSetLabel; private javax.swing.JCheckBox sliceAutorangesCB; private org.jdesktop.beansbinding.BindingGroup bindingGroup; // End of variables declaration//GEN-END:variables }