package com.aionemu.packetsamurai.gui.protocoleditor;
import java.awt.Color;
import java.awt.Component;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JDialog;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.JTree;
import javax.swing.border.LineBorder;
import javax.swing.event.MouseInputListener;
import javax.swing.event.TreeExpansionEvent;
import javax.swing.event.TreeExpansionListener;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.tree.DefaultMutableTreeNode;
import javax.swing.tree.DefaultTreeCellRenderer;
import javax.swing.tree.DefaultTreeModel;
import javax.swing.tree.TreePath;
import javolution.util.FastList;
import org.jdesktop.swingx.JXTree;
import org.jdesktop.swingx.JXTreeTable;
import org.jdesktop.swingx.treetable.TreeTableModel;
import com.aionemu.packetsamurai.PacketSamurai;
import com.aionemu.packetsamurai.gui.IconComboBoxRenderer;
import com.aionemu.packetsamurai.gui.Main;
import com.aionemu.packetsamurai.gui.images.IconsTable;
import com.aionemu.packetsamurai.parser.PartType;
import com.aionemu.packetsamurai.parser.PartTypeManager;
import com.aionemu.packetsamurai.parser.formattree.ForPart;
import com.aionemu.packetsamurai.parser.formattree.Part;
import com.aionemu.packetsamurai.parser.formattree.SwitchCaseBlock;
import com.aionemu.packetsamurai.protocol.protocoltree.PacketFamilly;
import com.aionemu.packetsamurai.protocol.protocoltree.PacketFormat;
import com.aionemu.packetsamurai.protocol.protocoltree.ProtocolNode;
@SuppressWarnings("serial")
/**
* This class is a JPanel that is used to represent a root packet family (like client packets or server packets)
* it is used in the ProtocolEditor
* @author Gilles Duboscq
*
*/
public class ProtocolTab extends JPanel
{
private JXTreeTable _partsTreeTable;
private PacketFamilly _packetFamilly;
private JXTree _protocolTree;
private JScrollPane _scrollPanePacketFormat;
public final static ImageIcon packetIcon = IconsTable.ICON_PACKET;
public final static ImageIcon packetGroupIcon = IconsTable.ICON_PACKETGRP;
public final static ImageIcon caseIcon = IconsTable.ICON_CASE;
private JComboBox _partTypeCombo;
public ProtocolTab(PacketFamilly familly)
{
this();
changeFamilly(familly);
}
public ProtocolTab()
{
setLayout(new GridLayout(1,2));
_scrollPanePacketFormat = new JScrollPane(createTreeTable(null));
_protocolTree = new JXTree();
_protocolTree.setCellRenderer(new PacketTreeRenderer());
_protocolTree.getSelectionModel().addTreeSelectionListener(new ProtocolTreeSelectionListener(_scrollPanePacketFormat));
JScrollPane scrollPaneProtocolTree = new JScrollPane(_protocolTree);
Container partsContainer = new Container();
GridBagConstraints partsCons = new GridBagConstraints();
GridBagLayout partsLayout = new GridBagLayout();
partsContainer.setLayout(partsLayout);
Container packetsContainer = new Container();
GridBagConstraints packetsCons = new GridBagConstraints();
GridBagLayout packetsLayout = new GridBagLayout();
packetsContainer.setLayout(packetsLayout);
Container partsToolBar = new Container();
Container packetsToolBar = new Container();
_partTypeCombo = new JComboBox(IconComboBoxRenderer.types);
_partTypeCombo.setRenderer(new IconComboBoxRenderer());
JButton addPartButton = new JButton(IconsTable.ICON_PLUS);
addPartButton.addActionListener(new PacketEditorActionListener());
addPartButton.setActionCommand("+");
JButton removePartButton = new JButton(IconsTable.ICON_MINUS);
removePartButton.addActionListener(new PacketEditorActionListener());
removePartButton.setActionCommand("-");
partsToolBar.add(_partTypeCombo);
partsToolBar.add(addPartButton);
partsToolBar.add(removePartButton);
FlowLayout partsFlowLayout = new FlowLayout();
partsFlowLayout.setAlignment(FlowLayout.CENTER);
partsToolBar.setLayout(partsFlowLayout);
JButton addPacketButton = new JButton(IconsTable.ICON_PLUS);
addPacketButton.addActionListener(new PacketEditorActionListener());
addPacketButton.setActionCommand("+p");
JButton removePacketButton = new JButton(IconsTable.ICON_MINUS);
removePacketButton.addActionListener(new PacketEditorActionListener());
removePacketButton.setActionCommand("-p");
packetsToolBar.add(addPacketButton);
packetsToolBar.add(removePacketButton);
FlowLayout packetsFlowLayout = new FlowLayout();
packetsFlowLayout.setAlignment(FlowLayout.CENTER);
packetsToolBar.setLayout(packetsFlowLayout);
packetsCons.weightx = 0.5;
packetsCons.fill = GridBagConstraints.BOTH;
packetsCons.gridy = 0;
packetsCons.gridx = 0;
packetsContainer.add(packetsToolBar,packetsCons);
packetsCons.gridy = 1;
packetsCons.weighty = 0.5;
packetsContainer.add(scrollPaneProtocolTree,packetsCons);
partsCons.weightx = 0.5;
partsCons.fill = GridBagConstraints.BOTH;
partsCons.gridy = 0;
partsCons.gridx = 0;
partsContainer.add(partsToolBar,partsCons);
partsCons.gridy = 1;
partsCons.weighty = 0.5;
partsContainer.add(_scrollPanePacketFormat,partsCons);
add(packetsContainer);
add(partsContainer);
}
public void changeFamilly(PacketFamilly f)
{
_packetFamilly = f;
if(f == null)
{
setName("-! No Protocol !-");
DefaultMutableTreeNode root = new DefaultMutableTreeNode("-");
_protocolTree.setModel(new DefaultTreeModel(root));
}
else
{
setName(f.getName());
DefaultMutableTreeNode root = new DefaultMutableTreeNode(_packetFamilly);
buildTree(_packetFamilly, root);
_protocolTree.setModel(new DefaultTreeModel(root));
}
}
private void buildTree(PacketFamilly root, DefaultMutableTreeNode currentNode)
{
FastList<ProtocolNode> nodes = new FastList<ProtocolNode>(root.getNodes().values());
Collections.sort(nodes, new Comparator<ProtocolNode>()
{
public int compare(ProtocolNode o1, ProtocolNode o2)
{
return o1.getID() - o2.getID();
}
});
for(ProtocolNode pNode : nodes)
{
DefaultMutableTreeNode newNode = new DefaultMutableTreeNode(pNode);
if(pNode instanceof PacketFormat)
{
currentNode.add(newNode);
}
else if(pNode instanceof PacketFamilly)
{
currentNode.add(newNode);
buildTree((PacketFamilly)pNode, newNode);
}
}
}
private JXTreeTable createTreeTable(PacketFormat format)
{
JXTreeTable partsTreeTable = new JXTreeTable(new PacketPartsTreeTableModel(format));
partsTreeTable.setDefaultEditor(TreeTableModel.class, new TreeTableComboBoxCellEditor(partsTreeTable));
partsTreeTable.setDefaultEditor(String.class, new TreeTableTextCellEditor(partsTreeTable));
partsTreeTable.setDefaultEditor(Integer.class, new TreeTableTextCellEditor(partsTreeTable));
partsTreeTable.setTreeCellRenderer(new PacketPartsTreeRenderer(partsTreeTable, format));
partsTreeTable.setRootVisible(false);
partsTreeTable.addTreeExpansionListener(new PacketPartsTreeExpensionListener(partsTreeTable));
//partsTreeTable.getColumnModel().getColumn(2).setMaxWidth(25);
//partsTreeTable.getColumnModel().getColumn(3).setMaxWidth(115);
//partsTreeTable.getColumnModel().getColumn(3).setPreferredWidth(105);
resizeTreeColumn(partsTreeTable);
TreeTableComboBoxCellEditor editor = (TreeTableComboBoxCellEditor) partsTreeTable.getDefaultEditor(TreeTableModel.class);
JComboBox combo = editor.getComboBox();
combo.setRenderer(new IconComboBoxRenderer());
_partsTreeTable = partsTreeTable;
return partsTreeTable;
}
private static void resizeTreeColumn(JXTreeTable treeTable)
{
/*TableCellRenderer headerRenderer = treeTable.getTableHeader().getDefaultRenderer();
TableColumn column = treeTable.getColumnModel().getColumn(0);
Component comp = headerRenderer.getTableCellRendererComponent(
null, column.getHeaderValue(),
false, false, 0, 0);
int headerWidth = comp.getPreferredSize().width;
int cellWidth = treeTable.getMaximumSize().width;
cellWidth += 40; //offset for the comboBox
int newWith = Math.max(headerWidth, cellWidth);
column.setPreferredWidth(newWith);
column.setMinWidth(newWith);
column.setMaxWidth(newWith);*/
treeTable.sizeColumnsToFit(0);
}
private class PacketTreeRenderer extends DefaultTreeCellRenderer
{
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if(value instanceof DefaultMutableTreeNode)
{
value = ((DefaultMutableTreeNode)value).getUserObject();
}
if(value instanceof ProtocolNode)
{
if(value instanceof PacketFormat)
{
setIcon(packetIcon);
}
else if(value instanceof PacketFamilly)
{
setIcon(packetGroupIcon);
}
}
return this;
}
}
private class PacketPartsTreeRenderer extends DefaultTreeCellRenderer
{
public PacketPartsTreeRenderer(JXTreeTable partsTreeTable, PacketFormat format)
{
addMouseMotionListener(new PacketPartMouseMotionListener(partsTreeTable, format));
addMouseListener(new PacketPartMouseMotionListener(partsTreeTable, format));
}
public PacketPartsTreeRenderer()
{
}
public Component getTreeCellRendererComponent(JTree tree, Object value,
boolean sel, boolean expanded, boolean leaf, int row,
boolean hasFocus)
{
super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus);
if(value instanceof Part)
{
String com = ((Part)value).getComment();
if(com != null && !com.equals(""))
setToolTipText(com);
if(value instanceof SwitchCaseBlock)
{
setIcon(caseIcon);
}
else
{
ImageIcon ii = IconsTable.getInstance().getIconForPartType(((Part)value).getType());
if(ii != null)
setIcon(ii);
}
}
return this;
}
}
private class PacketPartsTreeExpensionListener implements TreeExpansionListener
{
private JXTreeTable _treeTable;
public PacketPartsTreeExpensionListener(JXTreeTable partsTreeTable)
{
_treeTable = partsTreeTable;
}
public void treeCollapsed(TreeExpansionEvent event)
{
resizeTreeColumn(_treeTable);
}
public void treeExpanded(TreeExpansionEvent event)
{
resizeTreeColumn(_treeTable);
}
}
private class ProtocolTreeSelectionListener implements TreeSelectionListener
{
private JScrollPane _scrollPanePacketFormat;
public ProtocolTreeSelectionListener(JScrollPane pane)
{
_scrollPanePacketFormat = pane;
}
public void valueChanged(TreeSelectionEvent e)
{
Object obj = ((DefaultMutableTreeNode) e.getPath().getLastPathComponent()).getUserObject();
if(obj instanceof PacketFormat)
{
_scrollPanePacketFormat.setViewportView(ProtocolTab.this.createTreeTable(((PacketFormat)obj)));
}
else
{
_scrollPanePacketFormat.setViewportView(ProtocolTab.this.createTreeTable(null));
}
}
}
private class PacketEditorActionListener implements ActionListener
{
public void actionPerformed(ActionEvent e)
{
if(_packetFamilly == null)
return;
if(e.getActionCommand().equals("+"))
{
PacketFormat format = (PacketFormat) _partsTreeTable.getTreeTableModel().getRoot();
if(format == null)
return;
PartType type = PartTypeManager.getInstance().getType((String) _partTypeCombo.getSelectedItem());
Part part = new Part(type,-1,"","","");
if(type == PartType.forBlock)
part = new ForPart(-2);
Object afterObject = null;
if(_partsTreeTable.getSelectedRowCount() > 0)
{
TreePath path = _partsTreeTable.getPathForRow(_partsTreeTable.getSelectedRow());
if(path != null)
afterObject = path.getLastPathComponent();
}
if(afterObject instanceof SwitchCaseBlock)
{
((SwitchCaseBlock)afterObject).addPart(part);
}
else
{
Part afterPart = null;
afterPart = (Part) afterObject;
if(afterPart == null)
format.getDataFormat().getMainBlock().addPart(part);
else
format.getDataFormat().getMainBlock().addPartAfter(part, afterPart);
}
_partsTreeTable.updateUI();
}
else if(e.getActionCommand().equals("-"))
{
PacketFormat format = (PacketFormat) _partsTreeTable.getTreeTableModel().getRoot();
if(format == null || _partsTreeTable.getSelectedRowCount() < 1)
return;
for(int row : _partsTreeTable.getSelectedRows())
{
TreePath path = _partsTreeTable.getPathForRow(row);
if(path == null)
continue;
if(path.getLastPathComponent() instanceof SwitchCaseBlock)
{
SwitchCaseBlock sCase = (SwitchCaseBlock) path.getLastPathComponent();
sCase.getContainingSwitch().removeCase(sCase);
continue;
}
Part part = (Part) path.getLastPathComponent();
if(part == null)
continue;
format.getDataFormat().getMainBlock().removePart(part);
}
_partsTreeTable.updateUI();
}
else if(e.getActionCommand().equals("+p"))
{
final JDialog createDialog = new JDialog(((Main) PacketSamurai.getUserInterface()).getProtocolEditor(),"Create new Packet");
JPanel createPanel = new JPanel();
JButton createButton = new JButton("Create");
createButton.setActionCommand("create");
final JTextField createField = new JTextField("(c)",30);
createButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
if("create".equals(e.getActionCommand()))
{
int idNumber = 0;
String pString = createField.getText();
if(pString == null)
{
createDialog.setVisible(false);
createDialog.dispose();
return;
}
List<PartType> ids = PacketFormat.getIdPartsInString(pString);
if(ids == null)
{
createDialog.dispose();
return;
}
JDialog idDialog = new JDialog(((Main) PacketSamurai.getUserInterface()).getProtocolEditor(),"Create new Packet");
PartsPanel idPanel = new PartsPanel();
for(PartType p : ids)
{
idPanel.addRow(new PartRowPanel(p));
idNumber++;
}
idPanel.updateRows();
JPanel idDialPanel = new JPanel();
idDialPanel.setLayout(new BoxLayout(idDialPanel,BoxLayout.PAGE_AXIS));
JButton finishButton = new JButton("Finish");
finishButton.setActionCommand("finish");
finishButton.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
// TODO Auto-generated method stub
}
});
idDialPanel.add(idPanel);
idDialPanel.add(finishButton);
idDialog.setSize(450, 75+idNumber*30);
idDialog.setResizable(false);
idDialog.setContentPane(idDialPanel);
idDialog.setLocationRelativeTo(createDialog);
createDialog.setVisible(false);
idDialog.setVisible(true);
}
}
});
createPanel.add(createField);
createPanel.add(createButton);
createDialog.setSize(450, 75);
createDialog.setResizable(false);
createDialog.setContentPane(createPanel);
createDialog.setLocationRelativeTo(((Main) PacketSamurai.getUserInterface()).getProtocolEditor());
createDialog.setVisible(true);
}
else if(e.getActionCommand().equals("-p"))
{
TreePath[] paths = _protocolTree.getSelectionPaths();
if(paths == null)
return;
for(TreePath path : paths)
{
if(path == null)
continue;
ProtocolNode node = (ProtocolNode) ((DefaultMutableTreeNode)path.getLastPathComponent()).getUserObject();
if(node == null)
continue;
_packetFamilly.remove(node);
}
DefaultMutableTreeNode root = (DefaultMutableTreeNode) _protocolTree.getModel().getRoot();
root.removeAllChildren();
buildTree(_packetFamilly, root);
_protocolTree.updateUI();
}
}
}
private class PacketPartMouseMotionListener implements MouseInputListener
{
@SuppressWarnings("unused")
private JXTreeTable _treeTable;
private Part _dragedObject;
private MouseEvent _firstMouseEvent;
private PacketFormat _format;
public PacketPartMouseMotionListener(JXTreeTable partsTreeTable, PacketFormat format)
{
_treeTable = partsTreeTable;
_format = format;
}
public void mousePressed(MouseEvent e)
{
if(_format == null)
return;
if(_dragedObject != null && (e.getModifiersEx() & MouseEvent.BUTTON3_DOWN_MASK) == MouseEvent.BUTTON3_DOWN_MASK)
{
//TODO: cancel
System.out.println("Cancel");
return;
}
_firstMouseEvent = e;
_dragedObject = getObjectAt(e.getX(), e.getY());
}
public void mouseReleased(MouseEvent e)
{
if(_format == null)
return;
//TODO: finish movement
System.out.println("Finish move");
_dragedObject = null;
}
public void mouseDragged(MouseEvent e)
{
if(_format == null)
return;
int dx = e.getX() - _firstMouseEvent.getX();
int dy = e.getY() - _firstMouseEvent.getY();
//TODO: move part
System.out.println("dragging: x="+e.getX()+" y="+e.getY()+" dx="+dx+"dy="+dy);
}
private Part getObjectAt(int x, int y)
{
return null;
}
public void mouseMoved(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
}
private class PartsPanel extends JPanel
{
private List<PartRowPanel> _idRows;
public PartsPanel()
{
_idRows = new FastList<PartRowPanel>();
this.setLayout(new BoxLayout(this,BoxLayout.PAGE_AXIS));
}
public void addRow(PartRowPanel row)
{
_idRows.add(row);
}
public void addRow(PartRowPanel row, int i)
{
_idRows.add(i,row);
}
public void removeRow(PartRowPanel row)
{
_idRows.remove(row);
}
public void updateRows()
{
this.removeAll();
for(PartRowPanel row : _idRows)
{
this.add(row);
}
}
}
private class PartRowPanel extends JPanel
{
private PartType _type;
private PartTypeComboBox _typeCombo;
public PartRowPanel(PartType type)
{
_type = type;
_typeCombo = new PartTypeComboBox();
_typeCombo.setSelectedItem(_type.getName());
JButton addButton = new JButton(IconsTable.ICON_PLUS);
addButton.setActionCommand("+id");
addButton.addActionListener(new PacketEditorActionListener());
JButton delButton = new JButton(IconsTable.ICON_MINUS);
delButton.setActionCommand("-id");
delButton.addActionListener(new PacketEditorActionListener());
this.add(_typeCombo);
this.add(addButton);
this.add(delButton);
this.setBackground(Color.CYAN);
this.setBorder(new LineBorder(Color.BLACK));
}
}
}