package org.yamcs.ui;
import io.netty.handler.codec.http.HttpMethod;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.io.ByteArrayInputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JDialog;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.ToolTipManager;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.TreePath;
import org.yamcs.api.MediaType;
import org.yamcs.api.YamcsConnectionProperties;
import org.yamcs.api.rest.RestClient;
import org.yamcs.xtce.Parameter;
import org.yamcs.xtce.ParameterEntry;
import org.yamcs.xtce.XtceDb;
/**
* Displays packets with their parameters in a hierarchical view, allowing the
* user to select parameters.
*
* Basic search facility restricts view to parameters which contain specified
* text, with mutliple terms separated by *.
*
* Tree behaviour is currently default, so selecting a packet will not
* automatically select all its parameters.
*
* @author atu
*
*/
public class ParameterSelectDialog extends JDialog implements ActionListener, KeyListener, TreeSelectionListener {
private static final long serialVersionUID = 201212121400L;
private YamcsConnectionProperties connectData;
private XtceDb xtcedb;
private final JTree treeView;
private JLabel errorLabel;
private FilterableXtceDbTreeModel tm;
private JTextField searchField;
private List<ParameterSelectDialogListener> listeners;
ListIndexBar bar;
public ParameterSelectDialog(JFrame parent, YamcsConnectionProperties ycd) {
this( parent, ycd, null );
}
public ParameterSelectDialog(JFrame parent, YamcsConnectionProperties ycd, XtceDb db) {
super(parent, "Parameter Selection", true);
xtcedb = db;
connectData = ycd;
listeners = new ArrayList<ParameterSelectDialogListener>();
JPanel searchButtonPanel = new JPanel();
searchButtonPanel.setLayout( new BorderLayout() );
getContentPane().add(searchButtonPanel, BorderLayout.NORTH);
searchButtonPanel.add( new JLabel( "Search" ), BorderLayout.WEST );
searchField = new JTextField( "" );
searchField.addKeyListener( this );
searchButtonPanel.add( searchField, BorderLayout.CENTER );
errorLabel = new JLabel( "Loading..." );
getContentPane().add( new JScrollPane( errorLabel ), BorderLayout.CENTER );
loadXtcedb();
tm = new FilterableXtceDbTreeModel( xtcedb );
treeView = new JTree( tm );
treeView.addTreeSelectionListener( this );
ToolTipManager.sharedInstance().registerComponent(treeView);
treeView.setCellRenderer( new XtceDbCellRenderer() );
treeView.setExpandsSelectedPaths(true);
if( xtcedb != null ) {
JScrollPane scrollPane = new JScrollPane( treeView );
scrollPane.setVerticalScrollBarPolicy( JScrollPane.VERTICAL_SCROLLBAR_ALWAYS );
getContentPane().add( scrollPane, BorderLayout.CENTER );
}
bar = new ListIndexBar( treeView.getRowCount() );
bar.setBackground( Color.white );
bar.setForeground( Color.BLUE );
bar.setOpaque( false );
getContentPane().add( bar, BorderLayout.EAST );
bar.addSelectionListener( new ListSelectionListener() {
@Override
public void valueChanged( ListSelectionEvent e ) {
int selectedIndex = e.getFirstIndex();
treeView.scrollRowToVisible( selectedIndex );
}
} );
// OK and Cancel buttons
JPanel buttonPanel = new JPanel();
getContentPane().add(buttonPanel, BorderLayout.SOUTH);
JButton button = new JButton("Add");
button.setActionCommand("add");
button.addActionListener(this);
getRootPane().setDefaultButton(button);
buttonPanel.add(button);
button = new JButton("Close");
button.setActionCommand("close");
button.addActionListener(this);
buttonPanel.add(button);
// Final setup then pack
setMinimumSize(new Dimension(350, 100));
setLocationRelativeTo(parent);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
pack();
searchField.requestFocusInWindow();
}
@Override
public void actionPerformed(ActionEvent e) {
String cmd=e.getActionCommand();
if ( "add".equals( cmd ) ) {
for( ParameterSelectDialogListener l : listeners ) {
l.parametersAdded( getSelectedParameterOpsNames() );
}
} else {
setVisible( false );
}
}
public void updateBar() {
bar.setItemCount( treeView.getRowCount() );
bar.clearMarkers();
int [] selectedRows = treeView.getSelectionRows();
if( selectedRows != null ) {
for( int row : selectedRows ) {
bar.addMarker( row );
}
}
}
@Override
public void valueChanged( TreeSelectionEvent e ) {
updateBar();
}
public void loadXtcedb() {
if( xtcedb != null ) {
return;
}
RestClient restClient = new RestClient(connectData);
try {
restClient.setAcceptMediaType(MediaType.JAVA_SERIALIZED_OBJECT);
restClient.setMaxResponseLength(10*1024*1024);//TODO make this configurable
byte[] serializedMdb = restClient.doRequest("/mdb/"+connectData.getInstance(), HttpMethod.GET).get();
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(serializedMdb));
Object o=ois.readObject();
xtcedb=(XtceDb) o;
ois.close();
setLoadSuccess();
} catch (Exception e) {
System.out.println( "Exception whilst getting mission database: "+e.getMessage() );
setLoadFail( e.getMessage() );
}
}
private void setLoadFail( String message ) {
if( treeView != null ) {
treeView.setVisible( false );
}
if( searchField != null ) {
searchField.setVisible( false );
}
errorLabel.setVisible( true );
errorLabel.setText( message );
}
private void setLoadSuccess() {
errorLabel.setVisible( false );
if( treeView != null ) {
treeView.setVisible( true );
}
if( searchField != null ) {
searchField.setVisible( true );
}
}
/**
* Convenience method to get selected Parameter opsnames.
* @return
*/
public List<String> getSelectedParameterOpsNames() {
List<String> params = new ArrayList<String>();
for( Parameter p : getSelectedParameters() ) {
params.add( p.getOpsName() );
}
return params;
}
/**
* Gets all selected ParameterEntry instances, ignoring any other selected
* object.
*
* @return List, always valid but may be empty.
*/
public List<Parameter> getSelectedParameters() {
// Assumes model has parameters as leaves
List<Parameter> params = new ArrayList<Parameter>();
TreePath[] selectedPaths = treeView.getSelectionPaths();
if( selectedPaths != null ) {
for( TreePath tp : selectedPaths ) {
Object node = tp.getLastPathComponent();
if( node instanceof ParameterEntry ) {
params.add( ((ParameterEntry)node).getParameter() );
}
}
}
return params;
}
/**
* Gets all selected objects.
*
* @return List, always valid but may be empty.
*/
public List<Object> getSelected() {
List<Object> selected = new ArrayList<Object>();
TreePath[] selectedPaths = treeView.getSelectionPaths();
if( selectedPaths != null ) {
for( TreePath tp : selectedPaths ) {
selected.add( tp.getLastPathComponent() );
}
}
return selected;
}
// KeyListener - for the searchField
@Override
public void keyPressed(KeyEvent arg0) { /* Ignore in favour of keyReleased */ }
@Override
public void keyTyped(KeyEvent arg0) { /* Ignore in favour of keyReleased */ }
@Override
public void keyReleased(KeyEvent arg0) {
// Make sure currently selected objects continue to be shown
TreePath [] selected = treeView.getSelectionPaths();
tm.setAlwaysShown( getSelected() );
tm.setFilterText( searchField.getText() );
// Re-select previously selected.
treeView.setSelectionPaths( selected );
updateBar();
}
public void addListener( ParameterSelectDialogListener l ) {
listeners.add( l );
}
public void removeListener( ParameterSelectDialogListener l ) {
listeners.remove( l );
}
/**
* Presents the dialog to the user to get the current selection.
*
* @return List of parameter opsnames if OK button clicked, null otherwise.
*/
public List<String> showDialog() {
setVisible( true );
/*if( returnValue == APPROVE_OPTION ) {
return getSelectedParameterOpsNames();
}*/
return null;
}
}
interface ParameterSelectDialogListener {
public void parametersAdded( List<String> opsnames );
}