001package jmri.jmrit; 002 003import java.awt.event.ActionEvent; 004import java.beans.PropertyChangeEvent; 005import java.util.ArrayList; 006 007import javax.swing.AbstractAction; 008import javax.swing.JButton; 009import javax.swing.JCheckBox; 010import javax.swing.JMenu; 011import javax.swing.JMenuItem; 012import javax.swing.JOptionPane; 013import javax.swing.JSeparator; 014import javax.annotation.CheckForNull; 015import jmri.jmrit.beantable.TablesSettings; 016 017import jmri.InstanceManager; 018import jmri.jmrit.throttle.ThrottleCreationAction; 019import jmri.jmrit.z21server.Z21serverCreationAction; 020import jmri.util.gui.GuiLafPreferencesManager; 021import jmri.util.JmriJFrame; 022import jmri.AddressedProgrammerManager; 023import jmri.GlobalProgrammerManager; 024import jmri.jmrit.swing.ToolsMenuAction; 025import jmri.jmrix.ConnectionStatus; 026import jmri.jmrix.ConnectionConfig; 027import jmri.jmrix.ConnectionConfigManager; 028 029/** 030 * Create a "Tools" menu containing the Jmri system-independent tools 031 * <p> 032 * As a best practice, we are migrating the action names (constructor arguments) 033 * out of this class and into the contructors themselves. 034 * 035 * @author Bob Jacobsen Copyright 2003, 2008 036 * @author Matthew Harris copyright (c) 2009 037 */ 038public class ToolsMenu extends JMenu { 039 040 ConnectionConfig serModeProCon = null; 041 ConnectionConfig opsModeProCon = null; 042 043 AbstractAction serviceAction = new jmri.jmrit.symbolicprog.tabbedframe.PaneProgAction(Bundle.getMessage("MenuItemDecoderProServiceProgrammer")); 044 AbstractAction opsAction = new jmri.jmrit.symbolicprog.tabbedframe.PaneOpsProgAction(Bundle.getMessage("MenuItemDecoderProOpsModeProgrammer")); 045 046 public ToolsMenu(String name) { 047 this(); 048 setText(name); 049 } 050 051 public ToolsMenu() { 052 053 super(); 054 055 setText(Bundle.getMessage("MenuTools")); 056 057 JMenu programmerMenu = new JMenu(Bundle.getMessage("MenuProgrammers")); 058 programmerMenu.add(new jmri.jmrit.simpleprog.SimpleProgAction()); 059 programmerMenu.add(serviceAction); 060 programmerMenu.add(opsAction); 061 programmerMenu.add(new jmri.jmrit.dualdecoder.DualDecoderToolAction()); 062 add(programmerMenu); 063 064 // disable programmer menu if there's no programmer manager 065 if (InstanceManager.getNullableDefault(jmri.AddressedProgrammerManager.class) == null 066 && InstanceManager.getNullableDefault(jmri.GlobalProgrammerManager.class) == null) { 067 programmerMenu.setEnabled(false); 068 } 069 070 JMenu tableMenu = new JMenu(Bundle.getMessage("MenuTables")); 071 072 ///tableMenu.add(tableMenu); /// <=== WHY? 073 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemTurnoutTable"), "jmri.jmrit.beantable.TurnoutTableTabAction")); 074 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSensorTable"), "jmri.jmrit.beantable.SensorTableTabAction")); 075 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLightTable"), "jmri.jmrit.beantable.LightTableTabAction")); 076 077 JMenu signalMenu = new JMenu(Bundle.getMessage("MenuSignals")); 078 signalMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSignalTable"), "jmri.jmrit.beantable.SignalHeadTableAction")); 079 signalMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSignalMastTable"), "jmri.jmrit.beantable.SignalMastTableAction")); 080 signalMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSignalGroupTable"), "jmri.jmrit.beantable.SignalGroupTableAction")); 081 signalMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSignalMastLogicTable"), "jmri.jmrit.beantable.SignalMastLogicTableAction")); 082 tableMenu.add(signalMenu); 083 084 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemReporterTable"), "jmri.jmrit.beantable.ReporterTableTabAction")); 085 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemMemoryTable"), "jmri.jmrit.beantable.MemoryTableAction")); 086 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemStringIOTable"), "jmri.jmrit.beantable.StringIOTableAction")); 087 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemRouteTable"), "jmri.jmrit.beantable.RouteTableAction")); 088 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLRouteTable"), "jmri.jmrit.beantable.LRouteTableAction")); 089 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixTable"), "jmri.jmrit.beantable.LogixTableAction")); 090 091 JMenu logixNG_Menu = new JMenu(Bundle.getMessage("MenuLogixNG")); 092 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGTable"), "jmri.jmrit.beantable.LogixNGTableAction")); 093 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGModuleTable"), "jmri.jmrit.beantable.LogixNGModuleTableAction")); 094 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGTableTable"), "jmri.jmrit.beantable.LogixNGTableTableAction")); 095 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGGlobalVariableTableAction"), "jmri.jmrit.beantable.LogixNGGlobalVariableTableAction")); 096 tableMenu.add(logixNG_Menu); 097 098 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemBlockTable"), "jmri.jmrit.beantable.BlockTableAction")); 099 if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed()) { // turn on or off in prefs 100 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemOBlockTable"), "jmri.jmrit.beantable.OBlockTableAction")); 101 } else { 102 tableMenu.add(new jmri.jmrit.beantable.OBlockTableAction(Bundle.getMessage("MenuItemOBlockTable"))); 103 } 104 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSectionTable"), "jmri.jmrit.beantable.SectionTableAction")); 105 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemTransitTable"), "jmri.jmrit.beantable.TransitTableAction")); 106 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemAudioTable"), "jmri.jmrit.beantable.AudioTableAction")); 107 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemIdTagTable"), "jmri.jmrit.beantable.IdTagTableTabAction")); 108 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemRailComTable"), "jmri.jmrit.beantable.RailComTableAction")); 109 110 tableMenu.add(new JSeparator()); 111 JMenuItem settingsItem = new JMenuItem(Bundle.getMessage("MenuItemTablesSettings")); 112 tableMenu.add(settingsItem); 113 settingsItem.addActionListener((ActionEvent e) -> { 114 JmriJFrame f = new JmriJFrame(Bundle.getMessage("MenuItemTablesSettings")); 115 f.getContentPane().setLayout(new java.awt.GridBagLayout()); 116 java.awt.GridBagConstraints c = new java.awt.GridBagConstraints(); 117 c.gridwidth = 1; 118 c.gridheight = 1; 119 c.fill = java.awt.GridBagConstraints.HORIZONTAL; 120 c.anchor = java.awt.GridBagConstraints.CENTER; 121 c.weightx = 1.0; 122 123 JCheckBox showTablesMenu = new JCheckBox(Bundle.getMessage("MenuItemAddTablesMenuToMainMenu")); 124 showTablesMenu.setSelected(TablesSettings.isMainMenuEnabled()); 125 c.gridx = 0; 126 c.gridy = 0; 127 f.getContentPane().add(showTablesMenu, c); 128 129 JButton saveButton = new JButton(Bundle.getMessage("ButtonSave")); 130 c.gridy = 1; 131 f.getContentPane().add(saveButton, c); 132 saveButton.addActionListener((java.awt.event.ActionEvent ev) -> { 133 TablesSettings.setMainMenuEnabled(showTablesMenu.isSelected()); 134 TablesSettings.save(); 135 JOptionPane.showMessageDialog(f, 136 Bundle.getMessage("RestartRequiredHint"), 137 Bundle.getMessage("RestartRequired"), 138 JOptionPane.INFORMATION_MESSAGE); 139 }); 140 141 f.pack(); 142 f.setVisible(true); 143 }); 144 145 add(tableMenu); 146 147 JMenu throttleMenu = new JMenu(Bundle.getMessage("MenuThrottles")); 148 ThrottleCreationAction.addNewThrottleItemsToThrottleMenu(throttleMenu); 149 150 throttleMenu.add(new jmri.jmrit.throttle.ThrottlesListAction(Bundle.getMessage("MenuItemThrottlesList"))); 151 throttleMenu.addSeparator(); 152 throttleMenu.add(new jmri.jmrit.throttle.StoreXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveThrottleLayout"))); 153 throttleMenu.add(new jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadThrottleLayout"))); 154 throttleMenu.addSeparator(); 155 throttleMenu.add(new jmri.jmrit.throttle.StoreDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveAsDefaultThrottleLayout"))); 156 throttleMenu.add(new jmri.jmrit.throttle.LoadDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadDefaultThrottleLayout"))); 157 //throttleMenu.addSeparator(); 158 //throttleMenu.add(new jmri.jmrit.throttle.ThrottlesPreferencesAction(Bundle.getMessage("MenuItemThrottlesPreferences"))); // now in tabbed preferences 159 throttleMenu.add(new JSeparator()); 160 throttleMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction(Bundle.getMessage("MenuItemStartWiThrottle"))); 161 add(throttleMenu); 162 163 // disable the throttle menu if there is no throttle Manager 164 if (jmri.InstanceManager.getNullableDefault(jmri.ThrottleManager.class) == null) { 165 throttleMenu.setEnabled(false); 166 } 167 168 AbstractAction consistAction = new jmri.jmrit.consisttool.ConsistToolAction(Bundle.getMessage("MenuItemConsistTool")); 169 170 add(consistAction); 171 172 // disable the consist tool if there is no consist Manager 173 jmri.ConsistManager consistManager = jmri.InstanceManager.getNullableDefault(jmri.ConsistManager.class); 174 if (consistManager == null) { 175 consistAction.setEnabled(false); 176 } else if (consistManager.canBeDisabled()) { 177 consistManager.registerEnableListener((value) -> { 178 consistAction.setEnabled(value); 179 }); 180 consistAction.setEnabled(consistManager.isEnabled()); 181 } 182 183 JMenu clockMenu = new JMenu(Bundle.getMessage("MenuClocks")); 184 clockMenu.add(new jmri.jmrit.simpleclock.SimpleClockAction(Bundle.getMessage("MenuItemSetupClock"))); 185 clockMenu.add(new jmri.jmrit.nixieclock.NixieClockAction(Bundle.getMessage("MenuItemNixieClock"))); 186 clockMenu.add(new jmri.jmrit.lcdclock.LcdClockAction(Bundle.getMessage("MenuItemLcdClock"))); 187 clockMenu.add(new jmri.jmrit.analogclock.AnalogClockAction(Bundle.getMessage("MenuItemAnalogClock"))); 188 clockMenu.add(new jmri.jmrit.pragotronclock.PragotronClockAction(Bundle.getMessage("MenuItemPragotronClock"))); 189 add(clockMenu); 190 191 add(new JSeparator()); 192 // single-pane tools 193 add(new jmri.jmrit.powerpanel.PowerPanelAction(Bundle.getMessage("MenuItemPowerControl"))); 194 add(new jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlAction(Bundle.getMessage("MenuItemTurnoutControl"))); 195 add(new jmri.jmrit.simplelightctrl.SimpleLightCtrlAction(Bundle.getMessage("MenuItemLightControl"))); 196 add(new jmri.jmrit.speedometer.SpeedometerAction(Bundle.getMessage("MenuItemSpeedometer"))); 197 add(new jmri.jmrit.swing.meter.MeterAction(Bundle.getMessage("MenuItemMeter"))); 198 add(new jmri.jmrit.sensorgroup.SensorGroupAction(Bundle.getMessage("MenuItemSensorGroup"))); 199 add(new jmri.jmrit.blockboss.BlockBossAction(Bundle.getMessage("MenuItemSimpleSignal"))); 200 add(new jmri.jmrit.sendpacket.SendPacketAction(Bundle.getMessage("MenuItemSendDCCPacket"))); 201 202 add(new JSeparator()); 203 // more complex multi-window tools 204 add(new jmri.jmrit.operations.OperationsMenu()); 205 add(new jmri.jmrit.dispatcher.DispatcherAction(Bundle.getMessage("MenuItemDispatcher"))); 206 add(new jmri.jmrit.timetable.swing.TimeTableAction(Bundle.getMessage("MenuItemTimeTable"))); 207 add(new jmri.jmrit.whereused.WhereUsedAction(Bundle.getMessage("MenuItemWhereUsed"))); 208 // CTC menu item with submenus 209 JMenu ctcMenu = new JMenu(Bundle.getMessage("MenuCTC")); 210 ctcMenu.add(new jmri.jmrit.ctc.editor.CtcEditorAction(Bundle.getMessage("MenuItemCTCEditor"))); 211 ctcMenu.add(new jmri.jmrit.ctc.CtcRunAction(Bundle.getMessage("MenuItemCTCMain"))); 212 add(ctcMenu); 213 // US&S CTC subsystem tools 214 add(new jmri.jmrit.ussctc.ToolsMenu()); 215 // add cab signals 216 add(new jmri.jmrit.cabsignals.CabSignalAction()); 217 218 add(new JSeparator()); 219 JMenu serverMenu = new JMenu(Bundle.getMessage("MenuServers")); 220 serverMenu.add(new jmri.web.server.WebServerAction()); 221 serverMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction()); 222 serverMenu.add(new Z21serverCreationAction()); 223 serverMenu.add(new JSeparator()); 224 serverMenu.add(new jmri.jmris.simpleserver.SimpleServerAction()); 225 serverMenu.add(new jmri.jmris.srcp.JmriSRCPServerAction()); 226 add(serverMenu); 227 228 add(new JSeparator()); 229 JMenu vsdMenu = new JMenu(Bundle.getMessage("MenuItemVSDecoder")); 230 vsdMenu.add(new jmri.jmrit.vsdecoder.VSDecoderCreationAction(Bundle.getMessage("MenuItemVSDecoderManager"))); 231 vsdMenu.add(new jmri.jmrit.vsdecoder.swing.ManageLocationsAction(Bundle.getMessage("MenuItemVSDecoderLocationManager"))); 232 vsdMenu.add(new jmri.jmrit.vsdecoder.swing.VSDPreferencesAction(Bundle.getMessage("MenuItemVSDecoderPreferences"))); 233 add(vsdMenu); 234 235 add(new JSeparator()); 236 // LogixNG menu 237 add(new jmri.jmrit.logixng.tools.swing.LogixNGMenu()); 238 239 // Enable or disable the service mode programmer menu items for the types of programmer available. 240 updateProgrammerStatus(null); 241 ConnectionStatus.instance().addPropertyChangeListener((PropertyChangeEvent e) -> { 242 if ((e.getPropertyName().equals("change")) || (e.getPropertyName().equals("add"))) { 243 log.debug("Received property {} with value {} ", e.getPropertyName(), e.getNewValue()); 244 updateProgrammerStatus(e); 245 } 246 }); 247 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(AddressedProgrammerManager.class), 248 evt -> { 249 AddressedProgrammerManager m = (AddressedProgrammerManager) evt.getNewValue(); 250 if (m != null) { 251 m.addPropertyChangeListener(this::updateProgrammerStatus); 252 } 253 updateProgrammerStatus(evt); 254 }); 255 InstanceManager.getList(AddressedProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 256 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(GlobalProgrammerManager.class), 257 evt -> { 258 GlobalProgrammerManager m = (GlobalProgrammerManager) evt.getNewValue(); 259 if (m != null) { 260 m.addPropertyChangeListener(this::updateProgrammerStatus); 261 } 262 updateProgrammerStatus(evt); 263 }); 264 InstanceManager.getList(GlobalProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 265 266 // add items given by ToolsMenuItem service provider 267 var newItemList = new ArrayList<ToolsMenuAction>(); 268 java.util.ServiceLoader.load(jmri.jmrit.swing.ToolsMenuAction.class).forEach((toolsMenuAction) -> { 269 newItemList.add(toolsMenuAction); 270 }); 271 if (!newItemList.isEmpty()) { 272 add(new JSeparator()); 273 newItemList.forEach((item) -> { 274 log.info("Adding Plug In \'{}\' to Tools Menu", item); 275 add(item); 276 }); 277 } 278 279 } 280 281 /** 282 * Enable or disable the service mode programmer menu items for the types of programmer 283 * available. 284 * 285 * Adapted from similar named function in @link jmri.jmrit.roster.swing.RosterFrame.java 286 * 287 * @param evt the triggering event; if not null and if a removal of a 288 * ProgrammerManager, care will be taken not to trigger the 289 * automatic creation of a new ProgrammerManager 290 */ 291 protected void updateProgrammerStatus(@CheckForNull PropertyChangeEvent evt) { 292 log.debug("Updating Programmer Status for property {}", (evt != null) ? evt.getPropertyName() : "null"); 293 ConnectionConfig oldServMode = serModeProCon; 294 ConnectionConfig oldOpsMode = opsModeProCon; 295 GlobalProgrammerManager gpm = null; 296 AddressedProgrammerManager apm = null; 297 298 // Find the connection that goes with the global programmer 299 // test that IM has a default GPM, or that event is not the removal of a GPM 300 if (InstanceManager.containsDefault(GlobalProgrammerManager.class) 301 || (evt != null 302 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(GlobalProgrammerManager.class)) 303 && evt.getNewValue() == null)) { 304 gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 305 log.trace("found global programming manager {}", gpm); 306 } 307 if (gpm != null) { 308 String serviceModeProgrammerName = gpm.getUserName(); 309 log.debug("GlobalProgrammerManager found of class {} name {} ", gpm.getClass(), serviceModeProgrammerName); 310 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 311 for (ConnectionConfig connection : ccm) { 312 log.debug("Checking connection name {}", connection.getConnectionName()); 313 if (connection.getConnectionName() != null && connection.getConnectionName().equals(serviceModeProgrammerName)) { 314 log.debug("Connection found for GlobalProgrammermanager"); 315 serModeProCon = connection; 316 } 317 } 318 }); 319 } 320 321 // Find the connection that goes with the addressed programmer 322 // test that IM has a default APM, or that event is not the removal of an APM 323 if (InstanceManager.containsDefault(AddressedProgrammerManager.class) 324 || (evt != null 325 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(AddressedProgrammerManager.class)) 326 && evt.getNewValue() == null)) { 327 apm = InstanceManager.getNullableDefault(AddressedProgrammerManager.class); 328 log.trace("found addressed programming manager {}", gpm); 329 } 330 if (apm != null) { 331 String opsModeProgrammerName = apm.getUserName(); 332 log.debug("AddressedProgrammerManager found of class {} name {} ", apm.getClass(), opsModeProgrammerName); 333 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 334 for (ConnectionConfig connection : ccm) { 335 log.debug("Checking connection name {}", connection.getConnectionName()); 336 if (connection.getConnectionName() != null && connection.getConnectionName().equals(opsModeProgrammerName)) { 337 log.debug("Connection found for AddressedProgrammermanager"); 338 opsModeProCon = connection; 339 } 340 } 341 }); 342 } 343 344 log.trace("start global check with {}, {}, {}", serModeProCon, gpm, (gpm != null ? gpm.isGlobalProgrammerAvailable() : "<none>")); 345 if (gpm != null && gpm.isGlobalProgrammerAvailable()) { 346 log.debug("service mode available"); 347 if (oldServMode == null) { 348 serviceAction.setEnabled(true); 349 firePropertyChange("setprogservice", "setEnabled", true); 350 } 351 } else { 352 // No service programmer available, disable menu 353 log.debug("no service programmer"); 354 if (oldServMode != null) { 355 serviceAction.setEnabled(false); 356 firePropertyChange("setprogservice", "setEnabled", false); 357 } 358 serModeProCon = null; 359 } 360 361 if (apm != null && apm.isAddressedModePossible()) { 362 log.debug("ops mode available"); 363 if (oldOpsMode == null) { 364 opsAction.setEnabled(true); 365 firePropertyChange("setprogops", "setEnabled", true); 366 } 367 } else { 368 // No ops mode programmer available, disable interface sections not available 369 log.debug("no ops mode programmer"); 370 if (oldOpsMode != null) { 371 opsAction.setEnabled(false); 372 firePropertyChange("setprogops", "setEnabled", false); 373 } 374 opsModeProCon = null; 375 } 376 } 377 378 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(jmri.jmrit.ToolsMenu.class); 379 380}