001package jmri.jmrix.openlcb.swing; 002 003import jmri.*; 004import jmri.jmrit.beantable.signalmast.SignalMastAddPane; 005import jmri.jmrit.catalog.NamedIcon; 006import jmri.SystemConnectionMemo; 007import jmri.util.ConnectionNameFromSystemName; 008 009import jmri.jmrix.openlcb.*; 010import jmri.jmrix.ConnectionConfig; 011import jmri.jmrix.ConnectionConfigManager; 012 013import java.awt.*; 014import java.text.DecimalFormat; 015import java.util.*; 016import java.util.ArrayList; 017import java.util.List; 018 019import javax.swing.*; 020import javax.swing.border.TitledBorder; 021import javax.annotation.Nonnull; 022 023import org.openide.util.lookup.ServiceProvider; 024import org.openlcb.swing.EventIdTextField; 025 026/** 027 * A pane for configuring OlcbSignalMast objects 028 * 029 * @see jmri.jmrit.beantable.signalmast.SignalMastAddPane 030 * @author Bob Jacobsen Copyright (C) 2018 031 * @since 4.11.2 032 */ 033public class OlcbSignalMastAddPane extends SignalMastAddPane { 034 035 public OlcbSignalMastAddPane() { 036 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 037 038 litEventID.setText("00.00.00.00.00.00.00.00"); 039 notLitEventID.setText("00.00.00.00.00.00.00.00"); 040 heldEventID.setText("00.00.00.00.00.00.00.00"); 041 notHeldEventID.setText("00.00.00.00.00.00.00.00"); 042 043 // populate the OpenLCB connections list before creating GUI components. 044 getOlcbConnections(); 045 046 // If the connections list has less than 2 items, don't show a selector. 047 // This maintains backward compatibility with previous versions. 048 if (olcbConnections != null && olcbConnections.size() > 1) { 049 // Connection selector 050 JPanel p = new JPanel(); 051 TitledBorder border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 052 border.setTitle(Bundle.getMessage("OlcbConnection")); 053 p.setBorder(border); 054 p.setLayout(new jmri.util.javaworld.GridLayout2(3, 1)); 055 056 p.add(connSelectionBox); 057 add(p); 058 } 059 060 // lit/unlit controls 061 JPanel p = new JPanel(); 062 p.setLayout(new BoxLayout(p, BoxLayout.X_AXIS)); 063 p.add(new JLabel(Bundle.getMessage("AllowUnLitLabel") + ": ")); 064 p.add(allowUnLit); 065 p.setAlignmentX(Component.LEFT_ALIGNMENT); 066 add(p); 067 068 // aspects controls 069 TitledBorder aspectsBorder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 070 aspectsBorder.setTitle(Bundle.getMessage("EnterAspectsLabel")); 071 JScrollPane allAspectsScroll = new JScrollPane(allAspectsPanel); 072 allAspectsScroll.setBorder(aspectsBorder); 073 add(allAspectsScroll); 074 075 JPanel p5; 076 077 // Lit 078 TitledBorder litborder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 079 litborder.setTitle(Bundle.getMessage("LitUnLit")); 080 JPanel pLit = new JPanel(); 081 pLit.setBorder(litborder); 082 pLit.setLayout(new BoxLayout(pLit, BoxLayout.Y_AXIS)); 083 084 p5 = new JPanel(); 085 p5.setLayout(new BoxLayout(p5, BoxLayout.X_AXIS)); 086 p5.add(new JLabel(Bundle.getMessage("LitLabel"))); 087 p5.add(Box.createHorizontalGlue()); 088 pLit.add(p5); 089 pLit.add(litEventID); 090 091 p5 = new JPanel(); 092 p5.setLayout(new BoxLayout(p5, BoxLayout.X_AXIS)); 093 p5.add(new JLabel(Bundle.getMessage("NotLitLabel"))); 094 p5.add(Box.createHorizontalGlue()); 095 pLit.add(p5); 096 pLit.add(notLitEventID); 097 098 add(pLit); 099 100 // Held 101 TitledBorder heldborder = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 102 heldborder.setTitle(Bundle.getMessage("HeldUnHeld")); 103 JPanel pHeld= new JPanel(); 104 pHeld.setBorder(heldborder); 105 pHeld.setLayout(new BoxLayout(pHeld, BoxLayout.Y_AXIS)); 106 107 p5 = new JPanel(); 108 p5.setLayout(new BoxLayout(p5, BoxLayout.X_AXIS)); 109 p5.add(new JLabel(Bundle.getMessage("HeldLabel"))); 110 p5.add(Box.createHorizontalGlue()); 111 pHeld.add(p5); 112 pHeld.add(heldEventID); 113 114 p5 = new JPanel(); 115 p5.setLayout(new BoxLayout(p5, BoxLayout.X_AXIS)); 116 p5.add(new JLabel(Bundle.getMessage("NotHeldLabel"))); 117 p5.add(Box.createHorizontalGlue()); 118 pHeld.add(p5); 119 pHeld.add(notHeldEventID); 120 121 add(pHeld); 122 123 // set up selection of connections, if needed 124 populateConnSelectionBox(); 125 126 } 127 128 /** {@inheritDoc} */ 129 @Override 130 @Nonnull public String getPaneName() { 131 return Bundle.getMessage("OlcbSignalMastPane"); 132 } 133 134 final JCheckBox allowUnLit = new JCheckBox(); 135 136 // This used to be called "disabledAspects", but that's misleading: it's actually a map of 137 // ALL aspects' "disabled" checkboxes, regardless of their enabled/disabled state. 138 LinkedHashMap<String, JCheckBox> allAspectsCheckBoxes = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT); 139 final LinkedHashMap<String, EventIdTextField> aspectEventIDs = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT); 140 final JPanel allAspectsPanel = new JPanel(); 141 final EventIdTextField litEventID = new EventIdTextField(); 142 final EventIdTextField notLitEventID = new EventIdTextField(); 143 final EventIdTextField heldEventID = new EventIdTextField(); 144 final EventIdTextField notHeldEventID = new EventIdTextField(); 145 146 JComboBox<String> connSelectionBox = new JComboBox<String>(); 147 148 OlcbSignalMast currentMast = null; 149 150 // Support for multiple OpenLCB connections with different prefixes 151 ArrayList<String> olcbConnections = null; 152 153 /** {@inheritDoc} */ 154 @Override 155 public void setAspectNames(@Nonnull SignalAppearanceMap map, @Nonnull SignalSystem sigSystem) { 156 Enumeration<String> aspectNames = map.getAspects(); 157 // update immediately 158 allAspectsCheckBoxes = new LinkedHashMap<>(NOTIONAL_ASPECT_COUNT); 159 allAspectsPanel.removeAll(); 160 while (aspectNames.hasMoreElements()) { 161 String aspectName = aspectNames.nextElement(); 162 JCheckBox disabled = new JCheckBox(aspectName); 163 allAspectsCheckBoxes.put(aspectName, disabled); 164 EventIdTextField eventID = new EventIdTextField(); 165 eventID.setText("00.00.00.00.00.00.00.00"); 166 aspectEventIDs.put(aspectName, eventID); 167 } 168 allAspectsPanel.setLayout(new BoxLayout(allAspectsPanel, BoxLayout.Y_AXIS)); 169 for (Map.Entry<String, JCheckBox> entry : allAspectsCheckBoxes.entrySet()) { 170 JPanel p1 = new JPanel(); 171 TitledBorder p1border = BorderFactory.createTitledBorder(BorderFactory.createLineBorder(Color.black)); 172 p1border.setTitle(entry.getKey()); // Note this is not I18N'd: as-is from xml file 173 p1.setBorder(p1border); 174 p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 175 176 // Attempt to load an icon for display 177 String iconLink = map.getImageLink(entry.getKey(), "default"); 178 if (iconLink == null || iconLink.isEmpty()) { 179 log.debug("Got empty image link for {}", entry.getKey()); 180 } else { 181 log.debug("Image link for {} is {}", entry.getKey(), iconLink); 182 if (!iconLink.contains("preference:")) { 183 // This logic copied from SignalMastItemPanel.java 184 iconLink = iconLink.substring(iconLink.indexOf("resources")); 185 } 186 NamedIcon n = null; 187 try { 188 n = new NamedIcon(iconLink, iconLink); 189 log.debug("Loaded icon {}", iconLink); 190 } catch (Exception e) { 191 log.debug("Got exception trying to load icon link {}: {}", iconLink, e.getMessage()); 192 } 193 // display icon 194 if (n != null) { 195 p1.add(new JLabel(n)); 196 } 197 } 198 199 JPanel p2 = new JPanel(); 200 p2.setLayout(new BoxLayout(p2, BoxLayout.Y_AXIS)); 201 // event ID text box 202 p2.add(aspectEventIDs.get(entry.getKey())); 203 // "Disable" checkbox 204 p2.add(entry.getValue()); 205 entry.getValue().setName(entry.getKey()); 206 entry.getValue().setText(Bundle.getMessage("DisableAspect")); 207 p1.add(p2); 208 allAspectsPanel.add(p1); 209 } 210 211 populateConnSelectionBox(); 212 213 litEventID.setText("00.00.00.00.00.00.00.00"); 214 notLitEventID.setText("00.00.00.00.00.00.00.00"); 215 heldEventID.setText("00.00.00.00.00.00.00.00"); 216 notHeldEventID.setText("00.00.00.00.00.00.00.00"); 217 218 allAspectsPanel.revalidate(); 219 } 220 221 /* 222 * Populate the list of valid OpenLCB connection names. 223 * Only connections that have "OpenLCB" or "LCC" as their manufacturer are added. 224 */ 225 private void getOlcbConnections() { 226 olcbConnections = null; 227 ConnectionConfig[] conns = null; 228 try { 229 conns = InstanceManager.getDefault(ConnectionConfigManager.class).getConnections(); 230 } catch (Exception e) { 231 log.info("No ConnectionConfigManager installed: Using default Olcb Connections"); 232 } 233 if (conns == null || conns.length == 0) { 234 log.debug("Found null or empty connections list"); 235 return; 236 } 237 for (int x = 0; x < conns.length; x++) { 238 ConnectionConfig cc = conns[x]; 239 log.debug("conns[{}]: name={} info={} adapter={} conn={} man={}", 240 x, cc.name(), cc.getInfo(), cc.getAdapter(), 241 cc.getConnectionName(), cc.getManufacturer()); 242 /* As this is the Olcb signal mast add pane, only show OpenLCB/LCC connections */ 243 String man = cc.getManufacturer(); 244 String name = cc.getConnectionName(); 245 if (man != null && name != null && !name.isEmpty() && 246 (man.equals("OpenLCB") || man.equals("LCC"))) { 247 if (olcbConnections == null) { 248 olcbConnections = new ArrayList<String>(); 249 } 250 olcbConnections.add(name); 251 } 252 } 253 } 254 255 /* 256 * Populate the GUI connection selection box, if there are 257 * multiple OpenLCB connections. 258 */ 259 private void populateConnSelectionBox() { 260 connSelectionBox.removeAllItems(); 261 if (olcbConnections == null || olcbConnections.size() < 2) { 262 return; 263 } 264 for (String conn : olcbConnections) { 265 connSelectionBox.addItem(conn); 266 } 267 if (currentMast == null) { 268 connSelectionBox.setEnabled(true); 269 return; 270 } 271 // set the selected connection based on the current mast 272 String mastPrefix = currentMast.getSystemPrefix(); 273 if (mastPrefix != null) { 274 for (String conn : olcbConnections) { 275 String connectionPrefix = ConnectionNameFromSystemName.getPrefixFromName(conn); 276 if (connectionPrefix != null && connectionPrefix.equals(mastPrefix)) { 277 connSelectionBox.setSelectedItem(conn); 278 break; 279 } 280 } 281 } 282 // Can't change connection on existing masts 283 connSelectionBox.setEnabled(false); 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public boolean canHandleMast(@Nonnull SignalMast mast) { 289 return mast instanceof OlcbSignalMast; 290 } 291 292 /** {@inheritDoc} */ 293 @Override 294 public void setMast(SignalMast mast) { 295 if (mast == null) { 296 currentMast = null; 297 // re-enable connection selector 298 populateConnSelectionBox(); 299 return; 300 } 301 302 if (! (mast instanceof OlcbSignalMast) ) { 303 log.error("mast was wrong type: {} {}", mast.getSystemName(), mast.getClass().getName()); 304 return; 305 } 306 307 currentMast = (OlcbSignalMast) mast; 308 List<String> disabledList = currentMast.getDisabledAspects(); 309 if (disabledList != null) { 310 for (String aspect : disabledList) { 311 if (allAspectsCheckBoxes.containsKey(aspect)) { 312 allAspectsCheckBoxes.get(aspect).setSelected(true); 313 } 314 } 315 } 316 for (String aspect : currentMast.getAllKnownAspects()) { 317 if (aspectEventIDs.get(aspect) == null) { 318 EventIdTextField eventID = new EventIdTextField(); 319 eventID.setText("00.00.00.00.00.00.00.00"); 320 aspectEventIDs.put(aspect, eventID); 321 } 322 if (currentMast.isOutputConfigured(aspect)) { 323 aspectEventIDs.get(aspect).setText(currentMast.getOutputForAppearance(aspect)); 324 } else { 325 aspectEventIDs.get(aspect).setText("00.00.00.00.00.00.00.00"); 326 } 327 } 328 329 litEventID.setText(currentMast.getLitEventId()); 330 notLitEventID.setText(currentMast.getNotLitEventId()); 331 heldEventID.setText(currentMast.getHeldEventId()); 332 notHeldEventID.setText(currentMast.getNotHeldEventId()); 333 334 allowUnLit.setSelected(currentMast.allowUnLit()); 335 336 // show current connection in selector 337 populateConnSelectionBox(); 338 339 log.debug("setMast({})", mast); 340 } 341 342 final DecimalFormat paddedNumber = new DecimalFormat("0000"); 343 344 /** {@inheritDoc} */ 345 @Override 346 public boolean createMast(@Nonnull String sigsysname, 347 @Nonnull String mastname, 348 @Nonnull String username) { 349 if (currentMast == null) { 350 // create a mast 351 String selItem = (String)connSelectionBox.getSelectedItem(); 352 String connectionPrefix = ConnectionNameFromSystemName.getPrefixFromName(selItem); 353 log.debug("selected name={}, prefix={}", selItem, connectionPrefix); 354 // if prefix is null, use default 355 if (connectionPrefix == null || connectionPrefix.isEmpty()) { 356 connectionPrefix = "M"; 357 } 358 359 String type = mastname.substring(11, mastname.length() - 4); 360 String name = connectionPrefix + "F$olm:" + sigsysname + ":" + type; 361 name += "($" + (paddedNumber.format(OlcbSignalMast.getLastRef() + 1)) + ")"; 362 log.debug("Creating mast: {}", name); 363 currentMast = new OlcbSignalMast(name); 364 if (!username.equals("")) { 365 currentMast.setUserName(username); 366 } 367 currentMast.setMastType(type); 368 InstanceManager.getDefault(jmri.SignalMastManager.class).register(currentMast); 369 } 370 371 // load a new or existing mast 372 for (Map.Entry<String, JCheckBox> entry : allAspectsCheckBoxes.entrySet()) { 373 if (entry.getValue().isSelected()) { 374 currentMast.setAspectDisabled(entry.getKey()); 375 } else { 376 currentMast.setAspectEnabled(entry.getKey()); 377 } 378 currentMast.setOutputForAppearance(entry.getKey(), aspectEventIDs.get(entry.getKey()).getText()); 379 } 380 381 currentMast.setLitEventId(litEventID.getText()); 382 currentMast.setNotLitEventId(notLitEventID.getText()); 383 currentMast.setHeldEventId(heldEventID.getText()); 384 currentMast.setNotHeldEventId(notHeldEventID.getText()); 385 386 currentMast.setAllowUnLit(allowUnLit.isSelected()); 387 return true; 388 } 389 390 391 @ServiceProvider(service = SignalMastAddPane.SignalMastAddPaneProvider.class) 392 static public class SignalMastAddPaneProvider extends SignalMastAddPane.SignalMastAddPaneProvider { 393 394 /** 395 * {@inheritDoc} 396 * Requires a valid OpenLCB connection 397 */ 398 @Override 399 public boolean isAvailable() { 400 for (SystemConnectionMemo memo : InstanceManager.getList(SystemConnectionMemo.class)) { 401 if (memo instanceof jmri.jmrix.can.CanSystemConnectionMemo) { 402 return true; 403 } 404 } 405 return false; 406 } 407 408 /** {@inheritDoc} */ 409 @Override 410 @Nonnull public String getPaneName() { 411 return Bundle.getMessage("OlcbSignalMastPane"); 412 } 413 414 /** {@inheritDoc} */ 415 @Override 416 @Nonnull public SignalMastAddPane getNewPane() { 417 return new OlcbSignalMastAddPane(); 418 } 419 } 420 421 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OlcbSignalMastAddPane.class); 422 423}