package mil.navy.nps.logger;
import java.awt.*;
import java.awt.event.*;
import java.awt.datatransfer.*;
import java.util.*;
import javax.swing.*;
import javax.swing.event.*;
import mil.navy.nps.dis.*;
/**
* A Frame for editing PDUs. Brings up the pdus and lets us see
* the contents and edit at least some PDU types.
*/
public class EditFrame extends JFrame
{
JPanel left; // left panel, used to hold scrolling list of pdus
JPanel right; // right panel, which holds two panels: upper for header, lower for pdu
JList scrollingList; // list of pdu objects
JScrollPane scrollPane; // Scrolling pane that contains the list of pdu objects
Vector pduBuffer; // all the pdus we're editing
DefaultListModel listModel; // List model--used to notify list when underlying data has changed
PduPlayer pduPlayer; // pointer back to pdu player
PduEditPanel headerEditor = null; // editor for header portion of PDU
PduEditPanel bodyEditor = null; // editor for body of PDU
JScrollPane bodyScroller = null; // Scroll pane that holds the body editor
JMenuBar menuBar; // menu bar
JMenu editMenu; // Edit menu
JMenuItem cut; // cut
JMenuItem copy; // copy
JMenuItem paste; // paste
/**
* constructor
*/
public EditFrame(PduPlayer pPlayer)
{
pduPlayer = pPlayer;
pduBuffer = pduPlayer.getPduBuffer();
this.setSize(520, 600);
// Add a menu, with cut, copy and paste commands
menuBar = new JMenuBar();
editMenu = new JMenu("Edit");
cut = new JMenuItem("Cut");
copy = new JMenuItem("Copy");
paste = new JMenuItem("Paste");
// Action listener for the cut menu item
cut.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
EditFrame.this.cutOperation();
}
});
// Action listener for the copy menu item
copy.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
EditFrame.this.copyOperation();
}
});
// Action listener for the paste menu item
paste.addActionListener( new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
EditFrame.this.pasteOperation();
}
});
// Keystroke accelerators for the menu items
cut.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_X, ActionEvent.CTRL_MASK));
copy.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_C, ActionEvent.CTRL_MASK));
paste.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_V, ActionEvent.CTRL_MASK));
editMenu.add(cut);
editMenu.add(copy);
editMenu.add(paste);
menuBar.add(editMenu);
this.setJMenuBar(menuBar);
// Put the last part of the class name (eg "EntityStatePDU"
// from "mil.navy.nps.dis.EntityStatePDU") into a scrolling
// list. We add these to the ListModel, a special class that
// can notify the JList when it has been modified.
listModel = new DefaultListModel();
for(int idx = 0; idx < pduBuffer.size(); idx++)
{
ProtocolDataUnit pdu = (ProtocolDataUnit)pduBuffer.elementAt(idx);
String fullName, endOfName;
int lastPeriodLocation;
fullName = pdu.getClass().getName();
lastPeriodLocation = fullName.lastIndexOf('.');
endOfName = (new Integer(idx)).toString() + " " + fullName.substring(lastPeriodLocation + 1);
listModel.addElement(endOfName);
}
// Put list into a scrolling list. By default the list is a multiple selection
// list, which allows more than one, discontinuous selections. We allow this
// for the possibility of cut & paste.
//scrollingList = new DNDList(listModel);
scrollingList = new JList(listModel);
// Nifty drawing of icons in the scrolling list, instead of just boring text
//PduCellRenderer cellRender = new PduCellRenderer();
//scrollingList.setCellRenderer(cellRender);
scrollPane = new JScrollPane(scrollingList);
scrollPane.setPreferredSize(new Dimension(220, 500));
// Adds event listener for list selection changes
scrollingList.addListSelectionListener(new ListSelectionListener()
{
public void valueChanged(ListSelectionEvent e)
{
EditFrame.this.selectionChanged(e);
}
});
// Put it together
left = new JPanel(); // LH side, with list of pdus
right = new JPanel(); // RH side, with editing panels
this.getContentPane().setLayout(new GridLayout(1,2));
left.add(new JLabel("PDUs"));
left.add(scrollPane);
this.getContentPane().add(left);
this.getContentPane().add(right);
right.setLayout(new GridLayout(2,1));
// Set up action handler for window close. This just updates the
// count of pdus when we stop editing--the count may have changed
// as a result of the copying and pasting.
this.addWindowListener( new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
pduPlayer.syncPduCount();
}
});
}
/**
* Editing operation: cut. Remove the selected items from
* the list, put them onto the system clipboard.
*/
public void cutOperation()
{
int selectedIndexes[];
this.copyOperation(); // puts everything onto clipboard
// Need to remove the selected items now
// find the selected indexes
selectedIndexes = scrollingList.getSelectedIndices();
// Remove the selected items
for(int idx = 0; idx < selectedIndexes.length; idx++)
{
// Go in reverse order, so the act of removing an entry
// doesn't screw up the indicies we just found.
pduBuffer.remove(selectedIndexes[selectedIndexes.length - 1 - idx]);
listModel.remove(selectedIndexes[selectedIndexes.length - 1 - idx]);
}
}
/**
* Editing operation: copy. Leave the selected items as is
* and put them onto the system clipboard.
*/
public void copyOperation()
{
int selectedIndexes[]; // places on the scrolling list the user has selected
Vector selectedPdus = new Vector(); // vector that holds pdus selected
PduSelection selection; // object used to transfer data to clipboard
Clipboard clipboard; // system-wide clipboard object
// find the selected indexes
selectedIndexes = scrollingList.getSelectedIndices();
// There's a parallel list of pdus; add the pdus at the given indexes to the
// list of pdus we want to copy.
for(int idx=0; idx < selectedIndexes.length; idx++)
{
selectedPdus.add(pduBuffer.elementAt(selectedIndexes[idx]));
}
// Handles
selection = new PduSelection(selectedPdus);
// System-wide clipboard. This means we can paste pdus to other applications,
// if they're aware of the PduSelection data flavor. We can also create
// local clipboards that are visible only within this application. But why
// not go for the gusto?
clipboard = Toolkit.getDefaultToolkit().getSystemClipboard();
clipboard.setContents(selection, selection);
}
/**
* Editing operation: paste. Remove the items
* from the system clipboard (if they're pdus)
* and add them to the current insertion point
* on the list
*/
public void pasteOperation()
{
Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); // System clipboard
Transferable data = clipboard.getContents(this); // contents of clipboard
DataFlavor pduFlavor = new DataFlavor(PduSelection.class, "PDUSelection");// format of data we want
ProtocolDataUnit pasteData[]; // ref to the data we want
try
{
int indexes[]; // selected indexes in scrolling list
indexes = scrollingList.getSelectedIndices();
// Get ref to the data we want. We pass in the DataFlavor we want the data
// in (pduFlavor). We get back an array of PDUs.
pasteData = (ProtocolDataUnit[]) (data.getTransferData(pduFlavor));
// Bleah. No good solutions here. The basic problem is that we need to
// determine where the paste operation is going to put the data on the
// clipboard. I'll declare by fiat that there can be only one selected
// index, and that we paste immediately BEFORE that index.
if(indexes.length == 1)
{
java.util.List pasteList = Arrays.asList(pasteData);
pduBuffer.addAll(indexes[0], pasteList);
// Rebuild the list display
for(int idx = 0; idx < pasteList.size(); idx++)
{
ProtocolDataUnit pdu = (ProtocolDataUnit)pasteList.get(idx);
String fullName, endOfName;
int lastPeriodLocation;
fullName = pdu.getClass().getName();
lastPeriodLocation = fullName.lastIndexOf('.');
endOfName = (new Integer(idx)).toString() + " " + fullName.substring(lastPeriodLocation + 1);
listModel.add(indexes[0] + idx, endOfName);
}
}
}
catch (Exception e)
{
System.out.println("cast error obtaining paste data from clipboard " + e);
}
}
/**
* Removes any editing panes from the RH pane. this
* is usually done in preparation for adding a new
* editing pane. Note that we do not repaint the pane;
* that should be done externally. This also has the
* side effect of updating the pdu being edited in the
* editor pane--any changes to text fields will be reflected
* back into the pdu.
*/
private void deselectEditorPane()
{
// Remove the header editor
if(headerEditor != null)
{
right.remove(headerEditor);
headerEditor.updatePdu();
headerEditor = null;
}
// remove the pdu-type specific editor
if(bodyScroller != null)
{
right.remove(bodyScroller);
bodyEditor.updatePdu();
bodyScroller = null;
bodyEditor = null;
}
}
/**
* This method handles list selection changes. In general, there
* are a few cases we have to handle:>p>
*
* 1) change from selecting a single item to another single item
* 2) Add or subtract from some set of selections larger than cardinality one
* 3) Go to no selection at all.
*/
public void selectionChanged(ListSelectionEvent e)
{
ProtocolDataUnit pdu; // pdu selected
int indexes[]; // array that holds indexes of all selected cells in list
// remove the old pdu editor
this.deselectEditorPane();
// Find the pdu selected
indexes = scrollingList.getSelectedIndices();
// Case 1: we have exactly one selected
if(indexes.length == 1)
{
ProtocolDataUnit selectedPdu = (ProtocolDataUnit)pduBuffer.elementAt(indexes[0]);
// Create new header editor pane
headerEditor = new HeaderEditor();
headerEditor.setPdu(selectedPdu);
// Body editor, unique for each type of pdu
bodyEditor = PduEditPanel.editPanelFactory(selectedPdu);
bodyEditor.setPdu(selectedPdu);
bodyScroller = new JScrollPane(bodyEditor);
right.add(headerEditor);
right.add(bodyScroller);
}
// Case 2, 3: there are multiple or no selections in the list. In these
// cases we should have no editor present, because an editor must be
// associated with one and only one pdu.
// Repaint everything. We need to call revalidate() to make sure all
// the layout managers are working.
this.getContentPane().validate();
this.repaint();
}
}