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("MenuItemRouteTable"), "jmri.jmrit.beantable.RouteTableAction")); 080 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLRouteTable"), "jmri.jmrit.beantable.LRouteTableAction")); 081 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixTable"), "jmri.jmrit.beantable.LogixTableAction")); 082 083 JMenu logixNG_Menu = new JMenu(Bundle.getMessage("MenuLogixNG")); 084 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGTable"), "jmri.jmrit.beantable.LogixNGTableAction")); 085 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGModuleTable"), "jmri.jmrit.beantable.LogixNGModuleTableAction")); 086 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGTableTable"), "jmri.jmrit.beantable.LogixNGTableTableAction")); 087 logixNG_Menu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemLogixNGGlobalVariableTableAction"), "jmri.jmrit.beantable.LogixNGGlobalVariableTableAction")); 088 tableMenu.add(logixNG_Menu); 089 090 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemBlockTable"), "jmri.jmrit.beantable.BlockTableAction")); 091 if (InstanceManager.getDefault(GuiLafPreferencesManager.class).isOblockEditTabbed()) { // turn on or off in prefs 092 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemOBlockTable"), "jmri.jmrit.beantable.OBlockTableAction")); 093 } else { 094 tableMenu.add(new jmri.jmrit.beantable.OBlockTableAction(Bundle.getMessage("MenuItemOBlockTable"))); 095 } 096 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemSectionTable"), "jmri.jmrit.beantable.SectionTableAction")); 097 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemTransitTable"), "jmri.jmrit.beantable.TransitTableAction")); 098 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemAudioTable"), "jmri.jmrit.beantable.AudioTableAction")); 099 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemIdTagTable"), "jmri.jmrit.beantable.IdTagTableTabAction")); 100 tableMenu.add(new jmri.jmrit.beantable.ListedTableAction(Bundle.getMessage("MenuItemRailComTable"), "jmri.jmrit.beantable.RailComTableAction")); 101 add(tableMenu); 102 103 JMenu throttleMenu = new JMenu(Bundle.getMessage("MenuThrottles")); 104 ThrottleCreationAction.addNewThrottleItemsToThrottleMenu(throttleMenu); 105 106 throttleMenu.add(new jmri.jmrit.throttle.ThrottlesListAction(Bundle.getMessage("MenuItemThrottlesList"))); 107 throttleMenu.addSeparator(); 108 throttleMenu.add(new jmri.jmrit.throttle.StoreXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveThrottleLayout"))); 109 throttleMenu.add(new jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadThrottleLayout"))); 110 throttleMenu.addSeparator(); 111 throttleMenu.add(new jmri.jmrit.throttle.StoreDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveAsDefaultThrottleLayout"))); 112 throttleMenu.add(new jmri.jmrit.throttle.LoadDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadDefaultThrottleLayout"))); 113 //throttleMenu.addSeparator(); 114 //throttleMenu.add(new jmri.jmrit.throttle.ThrottlesPreferencesAction(Bundle.getMessage("MenuItemThrottlesPreferences"))); // now in tabbed preferences 115 throttleMenu.add(new JSeparator()); 116 throttleMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction(Bundle.getMessage("MenuItemStartWiThrottle"))); 117 add(throttleMenu); 118 119 // disable the throttle menu if there is no throttle Manager 120 if (jmri.InstanceManager.getNullableDefault(jmri.ThrottleManager.class) == null) { 121 throttleMenu.setEnabled(false); 122 } 123 124 AbstractAction consistAction = new jmri.jmrit.consisttool.ConsistToolAction(Bundle.getMessage("MenuItemConsistTool")); 125 126 add(consistAction); 127 128 // disable the consist tool if there is no consist Manager 129 jmri.ConsistManager consistManager = jmri.InstanceManager.getNullableDefault(jmri.ConsistManager.class); 130 if (consistManager == null) { 131 consistAction.setEnabled(false); 132 } else if (consistManager.canBeDisabled()) { 133 consistManager.registerEnableListener((value) -> { 134 consistAction.setEnabled(value); 135 }); 136 consistAction.setEnabled(consistManager.isEnabled()); 137 } 138 139 JMenu clockMenu = new JMenu(Bundle.getMessage("MenuClocks")); 140 clockMenu.add(new jmri.jmrit.simpleclock.SimpleClockAction(Bundle.getMessage("MenuItemSetupClock"))); 141 clockMenu.add(new jmri.jmrit.nixieclock.NixieClockAction(Bundle.getMessage("MenuItemNixieClock"))); 142 clockMenu.add(new jmri.jmrit.lcdclock.LcdClockAction(Bundle.getMessage("MenuItemLcdClock"))); 143 clockMenu.add(new jmri.jmrit.analogclock.AnalogClockAction(Bundle.getMessage("MenuItemAnalogClock"))); 144 clockMenu.add(new jmri.jmrit.pragotronclock.PragotronClockAction(Bundle.getMessage("MenuItemPragotronClock"))); 145 add(clockMenu); 146 147 add(new JSeparator()); 148 // single-pane tools 149 add(new jmri.jmrit.powerpanel.PowerPanelAction(Bundle.getMessage("MenuItemPowerControl"))); 150 add(new jmri.jmrit.simpleturnoutctrl.SimpleTurnoutCtrlAction(Bundle.getMessage("MenuItemTurnoutControl"))); 151 add(new jmri.jmrit.simplelightctrl.SimpleLightCtrlAction(Bundle.getMessage("MenuItemLightControl"))); 152 add(new jmri.jmrit.speedometer.SpeedometerAction(Bundle.getMessage("MenuItemSpeedometer"))); 153 add(new jmri.jmrit.swing.meter.MeterAction(Bundle.getMessage("MenuItemMeter"))); 154 add(new jmri.jmrit.sensorgroup.SensorGroupAction(Bundle.getMessage("MenuItemSensorGroup"))); 155 add(new jmri.jmrit.blockboss.BlockBossAction(Bundle.getMessage("MenuItemSimpleSignal"))); 156 add(new jmri.jmrit.sendpacket.SendPacketAction(Bundle.getMessage("MenuItemSendDCCPacket"))); 157 158 add(new JSeparator()); 159 // more complex multi-window tools 160 add(new jmri.jmrit.operations.OperationsMenu()); 161 add(new jmri.jmrit.dispatcher.DispatcherAction(Bundle.getMessage("MenuItemDispatcher"))); 162 add(new jmri.jmrit.timetable.swing.TimeTableAction(Bundle.getMessage("MenuItemTimeTable"))); 163 add(new jmri.jmrit.whereused.WhereUsedAction(Bundle.getMessage("MenuItemWhereUsed"))); 164 // CTC menu item with submenus 165 JMenu ctcMenu = new JMenu(Bundle.getMessage("MenuCTC")); 166 ctcMenu.add(new jmri.jmrit.ctc.editor.CtcEditorAction(Bundle.getMessage("MenuItemCTCEditor"))); 167 ctcMenu.add(new jmri.jmrit.ctc.CtcRunAction(Bundle.getMessage("MenuItemCTCMain"))); 168 add(ctcMenu); 169 // US&S CTC subsystem tools 170 add(new jmri.jmrit.ussctc.ToolsMenu()); 171 // add cab signals 172 add(new jmri.jmrit.cabsignals.CabSignalAction()); 173 174 add(new JSeparator()); 175 JMenu serverMenu = new JMenu(Bundle.getMessage("MenuServers")); 176 serverMenu.add(new jmri.web.server.WebServerAction()); 177 serverMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction()); 178 serverMenu.add(new Z21serverCreationAction()); 179 serverMenu.add(new JSeparator()); 180 serverMenu.add(new jmri.jmris.simpleserver.SimpleServerAction()); 181 serverMenu.add(new jmri.jmris.srcp.JmriSRCPServerAction()); 182 add(serverMenu); 183 184 add(new JSeparator()); 185 JMenu vsdMenu = new JMenu(Bundle.getMessage("MenuItemVSDecoder")); 186 vsdMenu.add(new jmri.jmrit.vsdecoder.VSDecoderCreationAction(Bundle.getMessage("MenuItemVSDecoderManager"))); 187 vsdMenu.add(new jmri.jmrit.vsdecoder.swing.ManageLocationsAction(Bundle.getMessage("MenuItemVSDecoderLocationManager"))); 188 vsdMenu.add(new jmri.jmrit.vsdecoder.swing.VSDPreferencesAction(Bundle.getMessage("MenuItemVSDecoderPreferences"))); 189 add(vsdMenu); 190 191 add(new JSeparator()); 192 // LogixNG menu 193 add(new jmri.jmrit.logixng.tools.swing.LogixNGMenu()); 194 195 // Enable or disable the service mode programmer menu items for the types of programmer available. 196 updateProgrammerStatus(null); 197 ConnectionStatus.instance().addPropertyChangeListener((PropertyChangeEvent e) -> { 198 if ((e.getPropertyName().equals("change")) || (e.getPropertyName().equals("add"))) { 199 log.debug("Received property {} with value {} ", e.getPropertyName(), e.getNewValue()); 200 updateProgrammerStatus(e); 201 } 202 }); 203 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(AddressedProgrammerManager.class), 204 evt -> { 205 AddressedProgrammerManager m = (AddressedProgrammerManager) evt.getNewValue(); 206 if (m != null) { 207 m.addPropertyChangeListener(this::updateProgrammerStatus); 208 } 209 updateProgrammerStatus(evt); 210 }); 211 InstanceManager.getList(AddressedProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 212 InstanceManager.addPropertyChangeListener(InstanceManager.getListPropertyName(GlobalProgrammerManager.class), 213 evt -> { 214 GlobalProgrammerManager m = (GlobalProgrammerManager) evt.getNewValue(); 215 if (m != null) { 216 m.addPropertyChangeListener(this::updateProgrammerStatus); 217 } 218 updateProgrammerStatus(evt); 219 }); 220 InstanceManager.getList(GlobalProgrammerManager.class).forEach(m -> m.addPropertyChangeListener(this::updateProgrammerStatus)); 221 222 // add items given by ToolsMenuItem service provider 223 var newItemList = new ArrayList<ToolsMenuAction>(); 224 java.util.ServiceLoader.load(jmri.jmrit.swing.ToolsMenuAction.class).forEach((toolsMenuAction) -> { 225 newItemList.add(toolsMenuAction); 226 }); 227 if (!newItemList.isEmpty()) { 228 add(new JSeparator()); 229 newItemList.forEach((item) -> { 230 log.info("Adding Plug In \'{}\' to Tools Menu", item); 231 add(item); 232 }); 233 } 234 235 } 236 237 /** 238 * Enable or disable the service mode programmer menu items for the types of programmer 239 * available. 240 * 241 * Adapted from similar named function in @link jmri.jmrit.roster.swing.RosterFrame.java 242 * 243 * @param evt the triggering event; if not null and if a removal of a 244 * ProgrammerManager, care will be taken not to trigger the 245 * automatic creation of a new ProgrammerManager 246 */ 247 protected void updateProgrammerStatus(@CheckForNull PropertyChangeEvent evt) { 248 log.debug("Updating Programmer Status for property {}", (evt != null) ? evt.getPropertyName() : "null"); 249 ConnectionConfig oldServMode = serModeProCon; 250 ConnectionConfig oldOpsMode = opsModeProCon; 251 GlobalProgrammerManager gpm = null; 252 AddressedProgrammerManager apm = null; 253 254 // Find the connection that goes with the global programmer 255 // test that IM has a default GPM, or that event is not the removal of a GPM 256 if (InstanceManager.containsDefault(GlobalProgrammerManager.class) 257 || (evt != null 258 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(GlobalProgrammerManager.class)) 259 && evt.getNewValue() == null)) { 260 gpm = InstanceManager.getNullableDefault(GlobalProgrammerManager.class); 261 log.trace("found global programming manager {}", gpm); 262 } 263 if (gpm != null) { 264 String serviceModeProgrammerName = gpm.getUserName(); 265 log.debug("GlobalProgrammerManager found of class {} name {} ", gpm.getClass(), serviceModeProgrammerName); 266 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 267 for (ConnectionConfig connection : ccm) { 268 log.debug("Checking connection name {}", connection.getConnectionName()); 269 if (connection.getConnectionName() != null && connection.getConnectionName().equals(serviceModeProgrammerName)) { 270 log.debug("Connection found for GlobalProgrammermanager"); 271 serModeProCon = connection; 272 } 273 } 274 }); 275 } 276 277 // Find the connection that goes with the addressed programmer 278 // test that IM has a default APM, or that event is not the removal of an APM 279 if (InstanceManager.containsDefault(AddressedProgrammerManager.class) 280 || (evt != null 281 && evt.getPropertyName().equals(InstanceManager.getDefaultsPropertyName(AddressedProgrammerManager.class)) 282 && evt.getNewValue() == null)) { 283 apm = InstanceManager.getNullableDefault(AddressedProgrammerManager.class); 284 log.trace("found addressed programming manager {}", gpm); 285 } 286 if (apm != null) { 287 String opsModeProgrammerName = apm.getUserName(); 288 log.debug("AddressedProgrammerManager found of class {} name {} ", apm.getClass(), opsModeProgrammerName); 289 InstanceManager.getOptionalDefault(ConnectionConfigManager.class).ifPresent((ccm) -> { 290 for (ConnectionConfig connection : ccm) { 291 log.debug("Checking connection name {}", connection.getConnectionName()); 292 if (connection.getConnectionName() != null && connection.getConnectionName().equals(opsModeProgrammerName)) { 293 log.debug("Connection found for AddressedProgrammermanager"); 294 opsModeProCon = connection; 295 } 296 } 297 }); 298 } 299 300 log.trace("start global check with {}, {}, {}", serModeProCon, gpm, (gpm != null ? gpm.isGlobalProgrammerAvailable() : "<none>")); 301 if (gpm != null && gpm.isGlobalProgrammerAvailable()) { 302 log.debug("service mode available"); 303 if (oldServMode == null) { 304 serviceAction.setEnabled(true); 305 firePropertyChange("setprogservice", "setEnabled", true); 306 } 307 } else { 308 // No service programmer available, disable menu 309 log.debug("no service programmer"); 310 if (oldServMode != null) { 311 serviceAction.setEnabled(false); 312 firePropertyChange("setprogservice", "setEnabled", false); 313 } 314 serModeProCon = null; 315 } 316 317 if (apm != null && apm.isAddressedModePossible()) { 318 log.debug("ops mode available"); 319 if (oldOpsMode == null) { 320 opsAction.setEnabled(true); 321 firePropertyChange("setprogops", "setEnabled", true); 322 } 323 } else { 324 // No ops mode programmer available, disable interface sections not available 325 log.debug("no ops mode programmer"); 326 if (oldOpsMode != null) { 327 opsAction.setEnabled(false); 328 firePropertyChange("setprogops", "setEnabled", false); 329 } 330 opsModeProCon = null; 331 } 332 } 333 334 private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(jmri.jmrit.ToolsMenu.class); 335 336}