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