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(); } }