package org.autoplot.datasource; import java.awt.Color; import java.awt.Component; import java.awt.Graphics; import java.awt.Rectangle; import java.awt.image.BufferedImage; import java.text.ParseException; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.DefaultListModel; import javax.swing.Icon; import javax.swing.ImageIcon; import javax.swing.JLabel; import javax.swing.JList; import javax.swing.JOptionPane; import javax.swing.JTable; import javax.swing.JTextField; import javax.swing.ListCellRenderer; import javax.swing.ListSelectionModel; import javax.swing.SwingUtilities; import javax.swing.event.ListSelectionEvent; import javax.swing.event.ListSelectionListener; import javax.swing.table.DefaultTableModel; import javax.swing.table.TableCellRenderer; import org.das2.components.DasProgressPanel; import org.das2.datum.DatumRange; import org.das2.datum.DatumRangeUtil; import org.das2.datum.Units; import org.das2.datum.UnitsUtil; import org.das2.event.DataRangeSelectionEvent; import org.das2.util.LoggerManager; import org.das2.util.monitor.ProgressMonitor; import org.das2.qds.QDataSet; import org.das2.qds.SemanticOps; import org.autoplot.datasource.capability.TimeSeriesBrowse; import org.das2.datum.OrbitDatumRange; import org.das2.datum.Orbits; import org.das2.qds.ops.Ops; /** * The EventsList tool makes it easy to browse around a list of events. It * takes a URI for a dataset which can be represented as an events list, * and fires off timeRangeSelection events when an event is selected. * * @author jbf */ public class TimeRangeToolEventsList extends javax.swing.JPanel { private static final Logger logger= LoggerManager.getLogger("apdss.gui"); DataSource dss= null; QDataSet currentDataSet= null; TimeSeriesBrowse tsb= null; boolean hasIcons= false; /** * Creates new form TimeRangeToolEventsList */ public TimeRangeToolEventsList() { initComponents(); fillList(); jTable1.getSelectionModel().addListSelectionListener(new ListSelectionListener() { @Override public void valueChanged(ListSelectionEvent evt) { if (evt.getValueIsAdjusting()) { logger.finest("value is adjusting"); } else { ListSelectionModel lsm= jTable1.getSelectionModel(); if (tsb != null) { if (lsm.getMinSelectionIndex() == 0) { loadTsb(-1); } else if (lsm.getMaxSelectionIndex() == jTable1.getRowCount() - 1) { loadTsb(1); } } fireTableSelection(); Rectangle r = jTable1.getCellRect( lsm.getMinSelectionIndex(), 0, false ); r= r.union( jTable1.getCellRect( lsm.getMaxSelectionIndex(), 0, false ) ); if (r != null) { jTable1.scrollRectToVisible(r); } } } }); } /** * return the dataSetSelector, so that for example a button for bookmarks * can be added by clients that know about bookmarks. * @return the DataSetSelector component. */ public DataSetSelector getDataSetSelector() { return this.currentDataSetSelector; } /** * return true if the URI is an orbits file which is understood by the das2 datum package. * @param uri an Autoplot URI * @return true if it is an orbits file. */ private static boolean isOrbitsFile( String uri ) { if ( uri.contains("?") ) { return false; } else { return Orbits.isOrbitsFile(uri); } } /** * return the ith range in the current list. * @param i * @return */ private DatumRange getRange( int i ) { QDataSet ds1= currentDataSet.slice(i).trim(0,2); // it's a shame this doesn't get the units right... Units tu= (Units)((QDataSet)currentDataSet.property(QDataSet.BUNDLE_1)).property(QDataSet.UNITS,0); if ( ds1.value(0)//GEN-BEGIN:initComponents private void initComponents() { rescaleComboBox = new javax.swing.JComboBox(); jLabel1 = new javax.swing.JLabel(); prevButton = new javax.swing.JButton(); nextButton = new javax.swing.JButton(); currentDataSetSelector = new org.autoplot.datasource.DataSetSelector(); timeRangeTF = new javax.swing.JTextField(); timeRangeButton = new javax.swing.JButton(); jScrollPane2 = new javax.swing.JScrollPane(); jTable1 = new javax.swing.JTable(); rescaleComboBox.setEditable(true); rescaleComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "none", "-5%,105%", "-10%,110%", "-100%,200%", "-300%,400%" })); rescaleComboBox.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { rescaleComboBoxActionPerformed(evt); } }); jLabel1.setText("Rescale:"); jLabel1.setToolTipText("Expand the interval range"); prevButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/resources/prevPrev.png"))); // NOI18N prevButton.setToolTipText("Previous Interval"); prevButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { prevButtonActionPerformed(evt); } }); nextButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/resources/nextNext.png"))); // NOI18N nextButton.setToolTipText("Next Interval"); nextButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { nextButtonActionPerformed(evt); } }); currentDataSetSelector.addFocusListener(new java.awt.event.FocusAdapter() { public void focusGained(java.awt.event.FocusEvent evt) { currentDataSetSelectorFocusGained(evt); } }); currentDataSetSelector.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { currentDataSetSelectorActionPerformed(evt); } }); timeRangeTF.setText(" "); timeRangeTF.setToolTipText("Load events from the given timerange"); timeRangeTF.setEnabled(false); timeRangeTF.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { timeRangeTFActionPerformed(evt); } }); timeRangeButton.setIcon(new javax.swing.ImageIcon(getClass().getResource("/org/autoplot/datasource/calendar.png"))); // NOI18N timeRangeButton.setToolTipText("Time Range Tool for setting the interval range"); timeRangeButton.setEnabled(false); timeRangeButton.addActionListener(new java.awt.event.ActionListener() { public void actionPerformed(java.awt.event.ActionEvent evt) { timeRangeButtonActionPerformed(evt); } }); jTable1.setModel(new javax.swing.table.DefaultTableModel( new Object [][] { {null, null, null, null}, {null, null, null, null}, {null, null, null, null}, {null, null, null, null} }, new String [] { "Title 1", "Title 2", "Title 3", "Title 4" } )); jTable1.addFocusListener(new java.awt.event.FocusAdapter() { public void focusLost(java.awt.event.FocusEvent evt) { jTable1FocusLost(evt); } }); jTable1.addMouseListener(new java.awt.event.MouseAdapter() { public void mouseClicked(java.awt.event.MouseEvent evt) { jTable1MouseClicked(evt); } }); jScrollPane2.setViewportView(jTable1); javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this); this.setLayout(layout); layout.setHorizontalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(layout.createSequentialGroup() .addContainerGap() .addComponent(jLabel1) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(rescaleComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 137, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(prevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 36, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(nextButton) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(timeRangeTF, javax.swing.GroupLayout.PREFERRED_SIZE, 196, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(timeRangeButton) .addContainerGap()) .addComponent(currentDataSetSelector, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE) .addComponent(jScrollPane2) ); layout.linkSize(javax.swing.SwingConstants.HORIZONTAL, new java.awt.Component[] {nextButton, prevButton}); layout.setVerticalGroup( layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup() .addComponent(currentDataSetSelector, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING) .addComponent(prevButton, javax.swing.GroupLayout.PREFERRED_SIZE, 27, javax.swing.GroupLayout.PREFERRED_SIZE) .addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE) .addComponent(rescaleComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(jLabel1) .addComponent(nextButton) .addComponent(timeRangeTF, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE) .addComponent(timeRangeButton))) .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED) .addComponent(jScrollPane2, javax.swing.GroupLayout.DEFAULT_SIZE, 270, Short.MAX_VALUE) .addContainerGap()) ); layout.linkSize(javax.swing.SwingConstants.VERTICAL, new java.awt.Component[] {nextButton, prevButton}); }// //GEN-END:initComponents /** Registers DataRangeSelectionListener to receive events. * @param listener The listener to register. */ public void addDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { listenerList.add(org.das2.event.DataRangeSelectionListener.class, listener); } /** Removes DataRangeSelectionListener from the list of listeners. * @param listener The listener to remove. */ public void removeDataRangeSelectionListener(org.das2.event.DataRangeSelectionListener listener) { listenerList.remove(org.das2.event.DataRangeSelectionListener.class, listener); } /** Notifies all registered listeners about the event. * * @param event The event to be fired */ private void fireDataRangeSelectionListenerDataRangeSelected(DataRangeSelectionEvent event) { Object[] listeners = listenerList.getListenerList(); for (int i = listeners.length-2; i>=0; i-=2) { if (listeners[i]==org.das2.event.DataRangeSelectionListener.class) { ((org.das2.event.DataRangeSelectionListener)listeners[i+1]).dataRangeSelected(event); } } } private void fireTableSelection() { int i1= jTable1.getSelectionModel().getMinSelectionIndex(); int i2= jTable1.getSelectionModel().getMaxSelectionIndex(); if ( currentDataSet==null ) return; DatumRange fire= null; String rescale= (String)rescaleComboBox.getSelectedItem(); for ( int i=i1; i<=i2; i++ ) { if ( jTable1.getSelectionModel().isSelectedIndex(i) ) { int isel= (tsb!=null) ? i-1 : i; if ( isel>=0 && ( currentDataSet==null || isel0 ) { try { fire= DatumRangeUtil.rescale( fire,rescale ); } catch (ParseException ex) { logger.log(Level.SEVERE, null, ex); } } fireDataRangeSelectionListenerDataRangeSelected( new DataRangeSelectionEvent(this,fire) ); } private void prevButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_prevButtonActionPerformed int i0= jTable1.getSelectionModel().getMinSelectionIndex(); int i1= jTable1.getSelectionModel().getMaxSelectionIndex(); if ( i0==-1 ) { i0= 0; i1= 0; } int de= i1-i0+1; i0= Math.max(0,i0-1); i1= i0 + de-1; jTable1.getSelectionModel().setSelectionInterval(i0,i1); }//GEN-LAST:event_prevButtonActionPerformed private void nextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextButtonActionPerformed int i0= jTable1.getSelectionModel().getMinSelectionIndex(); int i1= jTable1.getSelectionModel().getMaxSelectionIndex(); if ( i0==-1 ) { i0= 0; i1= 0; } int de= i1-i0+1; i1= Math.min( jTable1.getRowCount()-1,i1+1); i0= i1-de+1; //int[] indexes= new int[de]; //for ( int i=i0; i<=i1; i++ ) indexes[i-i0]= i; jTable1.getSelectionModel().setSelectionInterval(i0,i1); }//GEN-LAST:event_nextButtonActionPerformed private void rescaleComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_rescaleComboBoxActionPerformed //fireSelection(); fireTableSelection(); }//GEN-LAST:event_rescaleComboBoxActionPerformed /** * load the next or previous interval of events. * @param dir -1 for previous, +1 for next. */ private void loadTsb(final int dir) { jTable1.setEnabled(false); Runnable run= new Runnable() { @Override public void run() { try { DatumRange current= tsb.getTimeRange(); if ( dir==-1 ) { current= current.previous(); } else { current= current.next(); } loadViaTsb( current, dir ); } finally { jTable1.setEnabled(true); } } }; new Thread(run).start(); } /** * Load the events for the given range. * @param range range to load * @param dir -1 was going back to it, 1 is going ahead */ private void loadViaTsb( final DatumRange range, int dir ) { tsb.setTimeRange( range ); ProgressMonitor mon= DasProgressPanel.createFramed(SwingUtilities.getWindowAncestor(TimeRangeToolEventsList.this),"Loading Events File..."); try { QDataSet currentDataSet1= dss.getDataSet(mon.getSubtaskMonitor("Load Data")); currentDataSet1= makeCanonical(currentDataSet1); //TODO: someone is going to want to trim to this range. QDataSet rr= Ops.trim() currentDataSet= SemanticOps.trim( currentDataSet1, range, null ); } catch ( Exception ex ) { currentDataSet= null; } finally { mon.finished(); fillList(); final int i; if ( dir==-1 ) { i= jTable1.getRowCount()-2; } else if ( dir==1 ) { i= 1; } else { i= -1; // don't set anything } Runnable run= new Runnable() { public void run() { if ( i>0 ) { if ( currentDataSet!=null && currentDataSet.length()>0 ) { jTable1.getSelectionModel().setSelectionInterval(i,i); jTable1.setEnabled(true); } } timeRangeTF.setText( range.toString() ); } }; SwingUtilities.invokeLater(run); } } private void currentDataSetSelectorActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_currentDataSetSelectorActionPerformed final String uri= (String)currentDataSetSelector.getValue(); Runnable run= new Runnable() { @Override public void run() { ProgressMonitor mon= DasProgressPanel.createFramed(SwingUtilities.getWindowAncestor(TimeRangeToolEventsList.this),"Loading Events File..."); try { if ( isOrbitsFile(uri)) { // we'l read it twice but at least it's on the same thread. logger.fine("range events will be treated as orbits range events"); } TimeRangeToolEventsList.this.tsb= null; DataSource dsource = DataSetURI.getDataSource(uri); dss= dsource; QDataSet currentDataSet1= dss.getDataSet(mon); tsb= dsource.getCapability( TimeSeriesBrowse.class ); if ( tsb!=null ) { timeRangeTF.setText(tsb.getTimeRange().toString()); timeRangeTF.setEnabled(true); timeRangeButton.setEnabled(true); } else { timeRangeTF.setText(""); timeRangeTF.setEnabled(false); timeRangeButton.setEnabled(false); } currentDataSet1= makeCanonical(currentDataSet1); if ( tsb!=null ) { currentDataSet1= SemanticOps.trim( currentDataSet1, tsb.getTimeRange(), null ); } currentDataSet= currentDataSet1; hasIcons= false; if ( currentDataSet.length()>0 ) { double color0= currentDataSet.value(0,2); for ( int i=0; i