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