package me.legrange.panstamp.gui.view;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.ButtonGroup;
import javax.swing.JComponent;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JRadioButtonMenuItem;
import javax.swing.JSeparator;
import javax.swing.event.MenuEvent;
import javax.swing.event.MenuListener;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.tree.TreePath;
import me.legrange.panstamp.Endpoint;
import me.legrange.panstamp.Network;
import me.legrange.panstamp.NetworkException;
import me.legrange.panstamp.PanStamp;
import me.legrange.panstamp.Register;
import me.legrange.panstamp.gui.WaitDialog;
import me.legrange.panstamp.gui.model.tree.EndpointNode;
import me.legrange.panstamp.gui.model.tree.NetworkNode;
import me.legrange.panstamp.gui.model.tree.NetworkTreeNode;
import me.legrange.panstamp.gui.model.tree.PanStampNode;
import me.legrange.panstamp.gui.model.tree.RegisterNode;
import me.legrange.panstamp.gui.task.Task;
/**
*
* @author gideon
*/
public class Menus {
public JPopupMenu getPopupMenu(TreePath path) {
if (path != null) {
try {
NetworkTreeNode node = (NetworkTreeNode) path.getLastPathComponent();
selectedNode.set(node);
if (node != null) {
switch (node.getType()) {
case WORLD:
return getWorldPopupMenu();
case NETWORK:
return getNetworkPopupMenu();
case PANSTAMP:
return getPanStampPopupMenu();
case REGISTER:
return getRegisterPopupMenu();
case ENDPOINT:
return getEndpointPopupMenu();
}
}
} finally {
selectedNode.remove();
}
}
return null;
}
JMenu getWorldMenu() {
JMenu menu = new JMenu("Networks");
for (JMenuItem c : getWorldMenuItems()) {
menu.add(c);
}
menu.addMenuListener(menuEnableListener);
return menu;
}
JMenu getNetworkMenu() {
final JMenu menu = new JMenu("Network");
for (JMenuItem c : getNetworkMenuItems()) {
menu.add(c);
}
menu.addMenuListener(menuEnableListener);
return menu;
}
JMenu getPanStampMenu() {
JMenu menu = new JMenu("Device");
for (JComponent c : getPanStampMenuItems()) {
menu.add(c);
}
menu.addMenuListener(menuEnableListener);
return menu;
}
JMenu getRegisterMenu() {
JMenu menu = new JMenu("Register");
for (JComponent c : getRegisterMenuItems()) {
menu.add(c);
}
menu.addMenuListener(menuEnableListener);
return menu;
}
JMenu getEndpointMenu() {
JMenu menu = new JMenu("Endpoint");
for (JComponent c : getEndpointMenuItems()) {
menu.add(c);
}
menu.addMenuListener(menuEnableListener);
return menu;
}
Menus(View view) {
this.view = view;
}
private JPopupMenu getWorldPopupMenu() {
JPopupMenu menu = getWorldMenu().getPopupMenu();
menu.addPopupMenuListener(popupDisplayListener);
return menu;
}
private JPopupMenu getNetworkPopupMenu() {
JPopupMenu menu = getNetworkMenu().getPopupMenu();
menu.addPopupMenuListener(popupDisplayListener);
return menu;
}
private JPopupMenu getPanStampPopupMenu() {
JPopupMenu menu = getPanStampMenu().getPopupMenu();
menu.addPopupMenuListener(popupDisplayListener);
return menu;
}
private JPopupMenu getRegisterPopupMenu() {
JPopupMenu menu = getRegisterMenu().getPopupMenu();
menu.addPopupMenuListener(popupDisplayListener);
return menu;
}
private JPopupMenu getEndpointPopupMenu() {
JPopupMenu menu = getEndpointMenu().getPopupMenu();
menu.addPopupMenuListener(popupDisplayListener);
return menu;
}
private List<JMenuItem> getWorldMenuItems() {
List<JMenuItem> items = new LinkedList<>();
final JMenuItem addSerialItem = new JMenuItem("Add network...");
addSerialItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view.showNetworkAddDialog();
}
});
items.add(addSerialItem);
return items;
}
private List<JMenuItem> getNetworkMenuItems() {
List<JMenuItem> list = new LinkedList<>();
final JMenuItem openItem = new JMenuItem("Open") {
@Override
public boolean isEnabled() {
Network gw = getSelectedGateway();
return (gw != null) && (!gw.isOpen());
}
};
openItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
WaitDialog wd = new WaitDialog(null,
new Task() {
@Override
protected Object run() throws Throwable {
update(10, "Opening");
Network nw = getSelectedGateway();
nw.open();
return nw;
}
});
try {
wd.start();
} catch (Throwable ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
list.add(openItem);
final JMenuItem closeItem = new JMenuItem("Close") {
@Override
public boolean isEnabled() {
Network gw = getSelectedGateway();
return (gw != null) && (gw.isOpen());
}
};
closeItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
WaitDialog wd = new WaitDialog(null,
new Task() {
@Override
protected Object run() throws Throwable {
update(10, "Closing");
Network nw = getSelectedGateway();
nw.close();
return nw;
}
});
try {
wd.start();
} catch (Throwable ex) {
JOptionPane.showMessageDialog(null, ex.getMessage(), "Error", JOptionPane.ERROR_MESSAGE);
}
}
});
list.add(closeItem);
final JMenuItem deleteItem = new JMenuItem("Delete") {
@Override
public boolean isEnabled() {
return (getSelectedGateway() != null);
}
};
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
view.getModel().deleteGateway(getSelectedGateway());
} catch (NetworkException ex) {
Logger.getLogger(Menus.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
);
list.add(deleteItem);
return list;
}
/**
* Determine which gateway, if any, is currently selected in the tree view.
* A gateway is considered selected if a gateway node or one of it's
* descendants is selected.
*
* @return The selected gateway, or null if none is selected.
*/
private Network getSelectedGateway() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
switch (node.getType()) {
case WORLD:
return null;
case NETWORK:
return ((NetworkNode) node).getNetwork();
case PANSTAMP:
return ((PanStampNode) node).getPanStamp().getNetwork();
case REGISTER:
return ((RegisterNode) node).getRegister().getDevice().getNetwork();
case ENDPOINT:
return ((EndpointNode) node).getEndpoint().getRegister().getDevice().getNetwork();
default:
return null;
}
}
return null;
}
/**
* Determine which device, if any, is currently selected in the tree view. A
* device is considered selected if a device node or one of it's descendants
* is selected.
*
* @return The selected device, or null if none is selected.
*/
private PanStamp getSelectedDevice() {
PanStampNode psn = getSelectedPanStampNode();
if (psn != null) {
return psn.getPanStamp();
}
return null;
}
/**
* Determine which register, if any, is currently selected in the tree view.
* A register is considered selected if a register node or one of it's descendants
* is selected.
*
* @return The selected register, or null if none is selected.
*/
private Register getSelectedRegister() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
if (node.getType() == NetworkTreeNode.Type.REGISTER) {
return ((RegisterNode) node).getRegister();
}
}
return null;
}
/**
* Determine which endpoint, if any, is currently selected in the tree view.
* A endpoint is considered selected if a endpoint node is selected.
*
* @return The selected endpoint, or null if none is selected.
*/
private Endpoint getSelectedEndpoint() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
if (node.getType() == NetworkTreeNode.Type.ENDPOINT) {
return ((EndpointNode) node).getEndpoint();
}
}
return null;
}
private List<JComponent> getPanStampMenuItems() {
List<JComponent> list = new LinkedList<>();
final JMenuItem deleteItem = new JMenuItem("Delete") {
@Override
public boolean isEnabled() {
return (getSelectedDevice() != null);
}
};
deleteItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
PanStampNode psn = getSelectedPanStampNode();
if (psn != null) {
psn.getPanStamp().getNetwork().removeDevice(psn.getPanStamp().getAddress());
}
}
}
);
list.add(deleteItem);
list.add(new JSeparator());
final JMenuItem settingsItem = new JMenuItem("Settings...") {
@Override
public boolean isEnabled() {
PanStamp dev = getSelectedDevice();
return (dev != null) && dev.getNetwork().isOpen();
}
};
settingsItem.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e
) {
view.showPanStampSettingsDialog(getSelectedDevice());
}
}
);
final JMenuItem paramItem = new JMenuItem("Parameters...") {
@Override
public boolean isEnabled() {
PanStamp dev = getSelectedDevice();
if ((dev != null) && dev.getNetwork().isOpen()) {
for (Register reg : getSelectedDevice().getRegisters()) {
if (!reg.getParameters().isEmpty()) {
return true;
}
}
}
return false;
}
};
final JMenuItem graphItem = new JMenuItem("RSSI/LQI Graph...") {
@Override
public boolean isEnabled() {
PanStamp dev = getSelectedDevice();
return (dev != null) && (dev.getNetwork().isOpen());
}
};
graphItem.addActionListener(
new ActionListener() {
@Override
public void actionPerformed(ActionEvent e
) {
view.showSignalChart(getSelectedDevice());
}
}
);
list.add(settingsItem);
list.add(paramItem);
list.add(graphItem);
list.add(new JSeparator());
// register selection
list.add(getPanStampRegisterMenu());
// Disabled for now, have issue with OSX menu.
return list;
}
private List<JComponent> getRegisterMenuItems() {
List<JComponent> items = new LinkedList<>();
final JMenuItem reqItem = new JMenuItem("Request value") {
@Override
public boolean isEnabled() {
Register reg = getSelectedRegister();
return (reg != null) && reg.getDevice().getNetwork().isOpen();
}
};
reqItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
getSelectedRegister().requestValue();
} catch (NetworkException ex) {
Logger.getLogger(Menus.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
items.add(reqItem);
return items;
}
private List<JComponent> getEndpointMenuItems() {
List<JComponent> items = new LinkedList<>();
final JMenuItem setItem = new JMenuItem("Set value...") {
@Override
public boolean isEnabled() {
Endpoint ep = getSelectedEndpoint();
return (ep != null) && ep.getRegister().getDevice().getNetwork().isOpen() && ep.isOutput();
}
};
setItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view.showSetValueDialog(getSelectedEndpoint());
}
});
items.add(setItem);
final JMenuItem reqItem = new JMenuItem("Request value") {
@Override
public boolean isEnabled() {
Endpoint ep = getSelectedEndpoint();
return (ep != null) && ep.getRegister().getDevice().getNetwork().isOpen();
}
};
reqItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
try {
getSelectedEndpoint().requestValue();
} catch (NetworkException ex) {
Logger.getLogger(Menus.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
items.add(reqItem);
items.add(new JSeparator());
final JMenuItem graphItem = new JMenuItem("Data graph...") {
@Override
public boolean isEnabled() {
Endpoint ep = getSelectedEndpoint();
return (ep != null) && (ep.getRegister().getDevice().getNetwork().isOpen());
}
};
graphItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
view.showEndpointChart(getSelectedEndpoint());
}
});
items.add(graphItem);
return items;
}
private JMenu getPanStampRegisterMenu() {
final JMenu regsMenu = new JMenu("Show Standard Registers");
final JRadioButtonMenuItem allItem = getPanStampRegisterMenuItem("All", PanStampNode.RegisterDisplay.ALL);
final JRadioButtonMenuItem intItem = getPanStampRegisterMenuItem("Interesting", PanStampNode.RegisterDisplay.INTERESTING);
final JRadioButtonMenuItem noneItem = getPanStampRegisterMenuItem("None", PanStampNode.RegisterDisplay.NONE);
ButtonGroup group = new ButtonGroup();
group.add(allItem);
group.add(intItem);
group.add(noneItem);
regsMenu.add(allItem);
regsMenu.add(intItem);
noneItem.setSelected(true);
regsMenu.add(noneItem);
regsMenu.addMenuListener(new MenuListener() {
@Override
public void menuSelected(MenuEvent e) {
allItem.setSelected(allItem.isSelected());
intItem.setSelected(intItem.isSelected());
noneItem.setSelected(noneItem.isSelected());
}
@Override
public void menuDeselected(MenuEvent e) {
}
@Override
public void menuCanceled(MenuEvent e) {
}
});
return regsMenu;
}
private JRadioButtonMenuItem getPanStampRegisterMenuItem(String label, final PanStampNode.RegisterDisplay regD) {
JRadioButtonMenuItem item = new JRadioButtonMenuItem(label) {
@Override
public boolean isSelected() {
PanStampNode.RegisterDisplay rd = getSelectedRegisterDisplay();
return (rd != null) && (rd == regD);
}
@Override
public boolean isEnabled() {
PanStamp dev = getSelectedDevice();
return (dev != null) && (dev.getNetwork().isOpen());
}
};
item.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Component com : ((JMenuItem) e.getSource()).getParent().getComponents()) {
JMenuItem item = (JMenuItem) com;
item.setSelected(item == e.getSource());
}
setSelectedRegisterDisplay(PanStampNode.RegisterDisplay.valueOf(e.getActionCommand()));
}
});
item.setActionCommand(regD.name());
return item;
}
private PanStampNode.RegisterDisplay getSelectedRegisterDisplay() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
switch (node.getType()) {
case WORLD:
case NETWORK:
return null;
case PANSTAMP:
return ((PanStampNode) node).getRegisterDisplay();
case REGISTER:
return ((PanStampNode) ((RegisterNode) node).getParent()).getRegisterDisplay();
case ENDPOINT:
return ((PanStampNode) ((RegisterNode) ((EndpointNode) node).getParent()).getParent()).getRegisterDisplay();
default:
return null;
}
}
return null;
}
private void setSelectedRegisterDisplay(PanStampNode.RegisterDisplay rd) {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
PanStampNode psn = null;
switch (node.getType()) {
case PANSTAMP:
psn = ((PanStampNode) node);
break;
case REGISTER:
psn = ((PanStampNode) ((RegisterNode) node).getParent());
psn.setRegisterDisplay(rd);
break;
case ENDPOINT:
psn = ((PanStampNode) ((RegisterNode) ((EndpointNode) node).getParent()).getParent());
break;
default:
return;
}
psn.setRegisterDisplay(rd);
}
}
private PanStampNode getSelectedPanStampNode() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
switch (node.getType()) {
case PANSTAMP:
return (PanStampNode) node;
case REGISTER:
return (PanStampNode) node.getParent();
case ENDPOINT:
return (PanStampNode) node.getParent().getParent();
}
}
return null;
}
private NetworkNode getSelectedGatewayNode() {
NetworkTreeNode node = getSelectedNode();
if (node != null) {
switch (node.getType()) {
case NETWORK:
return (NetworkNode) node;
case PANSTAMP:
return (NetworkNode) node.getParent();
case REGISTER:
return (NetworkNode) node.getParent().getParent();
case ENDPOINT:
return (NetworkNode) node.getParent().getParent().getParent();
}
}
return null;
}
private NetworkTreeNode getSelectedNode() {
if (selectedNode.get() != null) {
return selectedNode.get();
} else if (view != null) {
TreePath path = view.getTree().getSelectionPath();
if (path != null) {
return (NetworkTreeNode) path.getLastPathComponent();
}
}
return null;
}
private final View view;
private final MenuListener menuEnableListener = new MenuListener() {
@Override
public void menuSelected(MenuEvent e) {
if (e.getSource() instanceof JMenu) {
JMenu menu = (JMenu) e.getSource();
int count = menu.getItemCount();
for (int i = 0; i < count; ++i) {
JMenuItem item = menu.getItem(i);
if (item != null) {
item.setEnabled(item.isEnabled());
}
}
}
}
@Override
public void menuDeselected(MenuEvent e) {
}
@Override
public void menuCanceled(MenuEvent e) {
}
};
private final PopupMenuListener popupDisplayListener = new PopupMenuListener() {
@Override
public void popupMenuWillBecomeVisible(PopupMenuEvent e) {
if (e.getSource() instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) e.getSource();
for (Component com : menu.getComponents()) {
if (com instanceof JMenuItem) {
JMenuItem item = (JMenuItem) com;
item.setEnabled(item.isEnabled());
}
}
}
}
@Override
public void popupMenuWillBecomeInvisible(PopupMenuEvent e) {
if (e.getSource() instanceof JPopupMenu) {
JPopupMenu menu = (JPopupMenu) e.getSource();
menu.removePopupMenuListener(this);
}
}
@Override
public void popupMenuCanceled(PopupMenuEvent e) {
}
};
private static final ThreadLocal<NetworkTreeNode> selectedNode = new ThreadLocal<>();
}