001package jmri.jmrix.openlcb.swing.networktree; 002 003import java.awt.Dimension; 004import javax.swing.JTree; 005import javax.swing.event.TreeSelectionEvent; 006import javax.swing.tree.DefaultMutableTreeNode; 007 008import jmri.*; 009import jmri.jmrix.can.CanListener; 010import jmri.jmrix.can.CanMessage; 011import jmri.jmrix.can.CanReply; 012import jmri.jmrix.can.CanSystemConnectionMemo; 013import jmri.jmrix.can.swing.CanPanelInterface; 014import jmri.jmrix.openlcb.swing.ClientActions; 015import jmri.util.JmriJFrame; 016import jmri.util.swing.JmriPanel; 017 018import org.openlcb.Connection; 019import org.openlcb.MimicNodeStore; 020import org.openlcb.NodeID; 021import org.openlcb.OlcbInterface; 022import org.openlcb.SimpleNodeIdent; 023import org.openlcb.implementations.MemoryConfigurationService; 024import org.openlcb.swing.memconfig.MemConfigDescriptionPane; 025import org.openlcb.swing.memconfig.MemConfigReadWritePane; 026import org.openlcb.swing.networktree.NodeTreeRep; 027import org.openlcb.swing.networktree.TreePane; 028 029/** 030 * Frame displaying tree of OpenLCB nodes. 031 * <p> 032 * This uses a {@link CanSystemConnectionMemo} for access to various 033 * org.openlcb.* 034 * OpenLCB context objects from the 035 * <a href="https://github.com/openlcb/OpenLCB_Java">OpenLCB_Java project</a>. 036 * The {@link org.openlcb.MimicNodeStore} fills out the tree of known nodes. 037 * When requested to configure a node, that node's CDI is loaded 038 * and presented using a {@link org.openlcb.swing.networktree.TreePane}. 039 * 040 * @author Bob Jacobsen Copyright (C) 2009, 2010, 2012, 2024 041 */ 042public class NetworkTreePane extends JmriPanel implements CanListener, CanPanelInterface { 043 044 public NetworkTreePane() { 045 super(); 046 } 047 048 private transient CanSystemConnectionMemo memo; 049 050 @Override 051 public void initContext(Object context) { 052 if (context instanceof CanSystemConnectionMemo) { 053 initComponents((CanSystemConnectionMemo) context); 054 } 055 } 056 057 @Override 058 public void initComponents(CanSystemConnectionMemo memo) { 059 this.memo = memo; 060 061 memo.getTrafficController().addCanListener(this); 062 063 // add GUI components 064 setLayout(new javax.swing.BoxLayout(this, javax.swing.BoxLayout.Y_AXIS)); 065 066 treePane = new TreePane(){ 067 UserPreferencesManager pref = jmri.InstanceManager.getDefault(UserPreferencesManager.class); 068 String sortPreferenceName = NetworkTreePane.class.getName() + ".selectedSortOrder"; 069 070 @Override 071 public void initComponents(MimicNodeStore store, final Connection connection, 072 final NodeID node, final NodeTreeRep.SelectionKeyLoader loader) { 073 super.initComponents(store, connection, node, loader); 074 // finally handle sort-by JComboBox preferences WITHOUT setting preferences 075 var name = pref.getProperty(this.getClass().getName(), sortPreferenceName); 076 if (name == null) name = "BY_NAME"; 077 SortOrder order; 078 try { 079 order = SortOrder.valueOf((String)name); 080 } catch (IllegalArgumentException e) { 081 order = SortOrder.BY_NAME; 082 } 083 super.setSortOrder(order); 084 // and do it a little later to make sure the table has been shown 085 final var localOrder = order; 086 jmri.util.ThreadingUtil.runOnLayoutDelayed( () -> { 087 super.setSortOrder(localOrder); 088 }, 750 089 ); 090 } 091 092 // This overrides setOrder to preserve the order 093 @Override 094 public void setSortOrder(SortOrder order) { 095 pref.setProperty(this.getClass().getName(), sortPreferenceName, order.name()); 096 super.setSortOrder(order); 097 } 098 }; 099 treePane.setPreferredSize(new Dimension(300, 300)); 100 101 treePane.initComponents(memo.get(MimicNodeStore.class), memo.get(Connection.class), memo.get(NodeID.class), new ActionLoader(memo) 102 ); 103 add(treePane); 104 105 treePane.addTreeSelectionListener((TreeSelectionEvent e) -> { 106 JTree tree = (JTree) e.getSource(); 107 DefaultMutableTreeNode node = (DefaultMutableTreeNode) tree.getLastSelectedPathComponent(); 108 109 if (node == null) { 110 return; 111 } 112 113 if (node.getUserObject() instanceof NodeTreeRep.SelectionKey) { 114 ((NodeTreeRep.SelectionKey) node.getUserObject()).select(node); 115 } 116 }); 117 } 118 119 TreePane treePane; 120 121 @Override 122 public String getTitle() { 123 if (memo != null) { 124 return (memo.getUserName() + " Network Tree"); 125 } 126 return "LCC / OpenLCB Network Tree"; 127 } 128 129 @Override 130 public void dispose() { 131 memo.getTrafficController().removeCanListener(this); 132 } 133 134 @Override 135 public synchronized void message(CanMessage l) { // receive a message and log it 136 } 137 138 @Override 139 public synchronized void reply(CanReply l) { // receive a reply and log it 140 } 141 142 //private final static Logger log = LoggerFactory.getLogger(NetworkTreePane.class); 143 144 /** 145 * Nested class to open specific windows when proper tree element is picked. 146 */ 147 private static class ActionLoader extends NodeTreeRep.SelectionKeyLoader { 148 149 private final ClientActions actions; 150 151 ActionLoader(CanSystemConnectionMemo memo) { 152 OlcbInterface iface = memo.get(OlcbInterface.class); 153 actions = new ClientActions(iface, memo); 154 this.store = iface.getNodeStore(); 155 this.mcs = iface.getMemoryConfigurationService(); 156 } 157 158 final MimicNodeStore store; 159 final MemoryConfigurationService mcs; 160 161 @Override 162 public NodeTreeRep.SelectionKey cdiKey(String name, NodeID node) { 163 return new NodeTreeRep.SelectionKey(name, node) { 164 @Override 165 public void select(DefaultMutableTreeNode rep) { 166 MimicNodeStore.NodeMemo memo = store.findNode(node); 167 SimpleNodeIdent ident = memo.getSimpleNodeIdent(); 168 StringBuilder description = new StringBuilder(); 169 if (ident.getUserName() != null) { 170 description.append(ident.getUserName()); 171 } 172 if (ident.getUserDesc() != null && ident.getUserDesc().length() > 0) { 173 if (description.length() > 0) { 174 description.append(" - "); 175 } 176 description.append(ident.getUserDesc()); 177 } 178 if (description.length() == 0) { 179 if (ident.getMfgName() != null && ident.getMfgName().length() > 0) { 180 description.append(ident.getMfgName()); 181 } 182 if (ident.getModelName() != null && ident.getModelName().length() > 0) { 183 if (description.length() > 0) { 184 description.append(" - "); 185 } 186 description.append(ident.getModelName()); 187 } 188 } 189 if (description.length() == 0) { 190 description.append(node.toString()); 191 } else { 192 description.append(" ("); 193 description.append(node.toString()); 194 description.append(")"); 195 } 196 openCdiPane(node, description.toString()); 197 } 198 }; 199 } 200 201 @Override 202 public NodeTreeRep.SelectionKey configurationKey(String name, NodeID node) { 203 return new NodeTreeRep.SelectionKey(name, node) { 204 @Override 205 public void select(DefaultMutableTreeNode rep) { 206 openConfigurePane(node); 207 } 208 }; 209 } 210 211 void openConfigurePane(NodeID node) { 212 { 213 JmriJFrame f = new JmriJFrame(); 214 f.setTitle("Configuration Capabilities " + node); 215 MemConfigDescriptionPane mc = new MemConfigDescriptionPane(node, store, mcs); 216 f.add(mc); 217 mc.initComponents(); 218 f.pack(); 219 f.setVisible(true); 220 } 221 { 222 JmriJFrame f = new JmriJFrame(); 223 f.setTitle("Configuration R/W Tool " + node); 224 MemConfigReadWritePane mc = new MemConfigReadWritePane(node, store, mcs); 225 f.add(mc); 226 mc.initComponents(); 227 f.pack(); 228 f.setVisible(true); 229 } 230 } 231 232 public void openCdiPane(final NodeID destNode, String description) { 233 actions.openCdiWindow(destNode, description); 234 } 235 } 236 237 // Create the panel-name of this node depending on what's available 238 static public String augmentedNodeName(MimicNodeStore.NodeMemo nodememo) { 239 var node = nodememo.getNodeID(); 240 var ident = nodememo.getSimpleNodeIdent(); 241 242 var description = new StringBuilder(); 243 if (ident.getUserName() != null) { 244 description.append(ident.getUserName()); 245 } 246 if (ident.getUserDesc() != null && ident.getUserDesc().length() > 0) { 247 if (description.length() > 0) { 248 description.append(" - "); 249 } 250 description.append(ident.getUserDesc()); 251 } 252 if (description.length() == 0) { 253 if (ident.getMfgName() != null && ident.getMfgName().length() > 0) { 254 description.append(ident.getMfgName()); 255 } 256 if (ident.getModelName() != null && ident.getModelName().length() > 0) { 257 if (description.length() > 0) { 258 description.append(" - "); 259 } 260 description.append(ident.getModelName()); 261 } 262 } 263 if (description.length() == 0) { 264 description.append(node.toString()); 265 } else { 266 description.append(" ("); 267 description.append(node.toString()); 268 description.append(")"); 269 } 270 return description.toString(); 271 } 272}