diff --git a/help/en/html/Sidebar b/help/en/html/Sidebar deleted file mode 100644 index cf2df1db090..00000000000 --- a/help/en/html/Sidebar +++ /dev/null @@ -1,60 +0,0 @@ - - -
-
-
JMRI® is...
-
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
Use JMRI to automate parts of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - - - - - - diff --git a/help/en/html/apps/index-compactside.shtml b/help/en/html/apps/index-compactside.shtml new file mode 100644 index 00000000000..a37bc0ec2bd --- /dev/null +++ b/help/en/html/apps/index-compactside.shtml @@ -0,0 +1,135 @@ + + + + + + + JMRI: Applications + + + + + + + + + + + +
+ +
+ + +

JMRI: Applications

+ +

JMRI provides several applications that are optimized for + specific purposes. You can of course also write your + own.

+ +
+
+ DecoderPro® +
+ +
A better tool for programming decoders. DecoderPro + simplifies the job of configuring complicated DCC decoders + by providing screens on which you can select the various + options and values you want.
+ Open DecoderPro by double clicking its icon in the JMRI + installation folder.
+ +
+ PanelPro™ +
+ +
Operating CRT based CTC control panels that reflect the + real-time state of your railroad and let you + control it - almost like a Prototype dispatcher!
+ Open PanelPro by double clicking its icon in the JMRI + installation folder.
+ +
+ DispatcherPro™ +
+ +
A system for Dispatching, grouping your Roster and + Throttles.
+ +
+ OperationsPro™ +
+ +
Create Train Manifests that detail the work your train + crews will perform during an operations session. The + Manifest lists of car pick up and set outs, shows where the + cars are located, and provides the cars' destinations.
+ +
+ SoundPro +
+ +
A set of tools for using Audio with JMRI.
+ Open SoundPro by double clicking its icon in the JMRI + installation folder.
+
+ +

Aditional Applications with JMRI Connections...

+ Hobbyists have created applications that work with JMRI + to provide extra capabilities. + +
+
+ Mainfest Creator +
+ +
Manifest Creator is an Excel-based + add-on to JMRI that can provide + train manifests and switch lists + in a number of formats.
+ +
+ TrainCrew +
+ +
TrainCrew allows engineers and conductors + using wireless throttles to add + realism to their operations.
+ +
+ CATS +
+ +
CATS provides a modern dispatching screen for + your model railroad.
+ +
+ + +

JMRI Tools

+ + +
+
+ + diff --git a/help/en/html/apps/index-newside.shtml b/help/en/html/apps/index-newside.shtml new file mode 100644 index 00000000000..4a20cc680e1 --- /dev/null +++ b/help/en/html/apps/index-newside.shtml @@ -0,0 +1,135 @@ + + + + + + + JMRI: Applications + + + + + + + + + + + +
+ +
+ + +

JMRI: Applications

+ +

JMRI provides several applications that are optimized for + specific purposes. You can of course also write your + own.

+ +
+
+ DecoderPro® +
+ +
A better tool for programming decoders. DecoderPro + simplifies the job of configuring complicated DCC decoders + by providing screens on which you can select the various + options and values you want.
+ Open DecoderPro by double clicking its icon in the JMRI + installation folder.
+ +
+ PanelPro™ +
+ +
Operating CRT based CTC control panels that reflect the + real-time state of your railroad and let you + control it - almost like a Prototype dispatcher!
+ Open PanelPro by double clicking its icon in the JMRI + installation folder.
+ +
+ DispatcherPro™ +
+ +
A system for Dispatching, grouping your Roster and + Throttles.
+ +
+ OperationsPro™ +
+ +
Create Train Manifests that detail the work your train + crews will perform during an operations session. The + Manifest lists of car pick up and set outs, shows where the + cars are located, and provides the cars' destinations.
+ +
+ SoundPro +
+ +
A set of tools for using Audio with JMRI.
+ Open SoundPro by double clicking its icon in the JMRI + installation folder.
+
+ +

Aditional Applications with JMRI Connections...

+ Hobbyists have created applications that work with JMRI + to provide extra capabilities. + +
+
+ Mainfest Creator +
+ +
Manifest Creator is an Excel-based + add-on to JMRI that can provide + train manifests and switch lists + in a number of formats.
+ +
+ TrainCrew +
+ +
TrainCrew allows engineers and conductors + using wireless throttles to add + realism to their operations.
+ +
+ CATS +
+ +
CATS provides a modern dispatching screen for + your model railroad.
+ +
+ + +

JMRI Tools

+ + +
+
+ + diff --git a/help/en/html/sidebar-compact.shtml b/help/en/html/sidebar-compact.shtml new file mode 100644 index 00000000000..4ecb4fbda9a --- /dev/null +++ b/help/en/html/sidebar-compact.shtml @@ -0,0 +1,32 @@ + + + +
+ +
+
JMRI® is...
+
+ + + +

Tools

+
JMRI provides powerful tools for working with your layout.
+ +

Layout Automation

+
Use JMRI to automate parts of your layout, from simply controlling a crossing gate + to running trains in the background.
+ +

Supported Hardware

+
JMRI supports a wide range of DCC systems, command stations and protocols.
+ +

JMRI Setup and Installation

+
JMRI can be installed on a variety of hardware and operating systems.
+ +
+ +
+ + + + + diff --git a/help/en/html/sidebar-new.shtml b/help/en/html/sidebar-new.shtml new file mode 100644 index 00000000000..9e0fd712533 --- /dev/null +++ b/help/en/html/sidebar-new.shtml @@ -0,0 +1,30 @@ + + +
+ +
+
JMRI® is...
+
+ + + + + + + + + + + + +
+ + + + + + + + + + diff --git a/help/en/html/tools/Sidebar b/help/en/html/tools/Sidebar deleted file mode 100644 index cf4d1e3bc5e..00000000000 --- a/help/en/html/tools/Sidebar +++ /dev/null @@ -1,82 +0,0 @@ - - -
-
-
JMRI is...
-
- -

Common Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

System-specific Tools

-
- -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - - - - - - diff --git a/help/en/html/tools/automation/Sidebar b/help/en/html/tools/automation/Sidebar deleted file mode 100644 index 1c84bfab8dd..00000000000 --- a/help/en/html/tools/automation/Sidebar +++ /dev/null @@ -1,64 +0,0 @@ - -
-
-
JMRI is...
-
- -

Scripting

-
Information on writing scripts to control JMRI in more detail: - -
- -

Tools

-
The general objects available to control your layout. - -
- -

Layout Automation

-
Other tools for JMRI Automation: - -
- -
- -
- - diff --git a/help/en/html/tools/consisttool/Sidebar b/help/en/html/tools/consisttool/Sidebar deleted file mode 100644 index 0c1b0d99b55..00000000000 --- a/help/en/html/tools/consisttool/Sidebar +++ /dev/null @@ -1,55 +0,0 @@ - -
-
-
JMRI is...
-
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/html/tools/fastclock/Sidebar b/help/en/html/tools/fastclock/Sidebar deleted file mode 100644 index 80a22c88f08..00000000000 --- a/help/en/html/tools/fastclock/Sidebar +++ /dev/null @@ -1,54 +0,0 @@ - -
-
-
JMRI is...
-
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/html/tools/programmer/Sidebar b/help/en/html/tools/programmer/Sidebar deleted file mode 100644 index 261904c1f7c..00000000000 --- a/help/en/html/tools/programmer/Sidebar +++ /dev/null @@ -1,55 +0,0 @@ - -
-
-
JMRI is...
-
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/html/tools/scripting/Sidebar b/help/en/html/tools/scripting/Sidebar deleted file mode 100644 index 1dfb56b3c44..00000000000 --- a/help/en/html/tools/scripting/Sidebar +++ /dev/null @@ -1,86 +0,0 @@ - - -
-
-
JMRI is...
-
- -

Scripting

-
Information on writing scripts to control JMRI in more detail: - -
- -

Python and Jython (General Info)

-
JMRI scripts are in Jython, a version of Python, a popular general-purpose computer language - -
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - - - - - - diff --git a/help/en/html/tools/signaling/Sidebar b/help/en/html/tools/signaling/Sidebar deleted file mode 100644 index de3760f43dc..00000000000 --- a/help/en/html/tools/signaling/Sidebar +++ /dev/null @@ -1,75 +0,0 @@ - - -
-
-
JMRI is...
-
- -

Signaling

-
Adding signals to your layout with JMRI. - -
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - - - - - - diff --git a/help/en/html/tools/signaling/interlock/Sidebar b/help/en/html/tools/signaling/interlock/Sidebar deleted file mode 100644 index 511ebd4bba0..00000000000 --- a/help/en/html/tools/signaling/interlock/Sidebar +++ /dev/null @@ -1,69 +0,0 @@ - - -
-
-
JMRI is...
-
- -

Signaling

-
Adding signals to your layout with JMRI. - -
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/html/tools/speedometer/Sidebar b/help/en/html/tools/speedometer/Sidebar deleted file mode 100644 index 3977413d126..00000000000 --- a/help/en/html/tools/speedometer/Sidebar +++ /dev/null @@ -1,60 +0,0 @@ - - -
-
-
JMRI is...
-
- -

Speedometer

-
Speed matching tool -
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/html/tools/throttle/Sidebar b/help/en/html/tools/throttle/Sidebar deleted file mode 100644 index d431cbf1745..00000000000 --- a/help/en/html/tools/throttle/Sidebar +++ /dev/null @@ -1,79 +0,0 @@ -
- -
- -
-

Throttles

-
-
- - - -
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- - - - - - -
diff --git a/help/en/html/tools/uss/Sidebar b/help/en/html/tools/uss/Sidebar deleted file mode 100644 index 9d0a5abece7..00000000000 --- a/help/en/html/tools/uss/Sidebar +++ /dev/null @@ -1,57 +0,0 @@ - - -
- -
-
JMRI is...
-
- -

Tools

-
JMRI provides powerful tools for working with your - layout. - -
- -

Layout Automation

-
JMRI can be used to automate parts - of your layout, from simply controlling a crossing gate - to running trains in the background. - -
- -
- -
- - diff --git a/help/en/parts/SidebarApplications.shtml b/help/en/parts/SidebarApplications.shtml index e921698e36e..f5a5c3fb4b8 100644 --- a/help/en/parts/SidebarApplications.shtml +++ b/help/en/parts/SidebarApplications.shtml @@ -1,13 +1,14 @@ - -

Applications

-
+ +

Applications

+
-
+ diff --git a/help/en/parts/SidebarSetup.shtml b/help/en/parts/SidebarSetup.shtml new file mode 100644 index 00000000000..5c0679381a2 --- /dev/null +++ b/help/en/parts/SidebarSetup.shtml @@ -0,0 +1,14 @@ + + +

JMRI Setup and Installation

+
+ +
+ + diff --git a/help/en/parts/SidebarSupportedHardware.shtml b/help/en/parts/SidebarSupportedHardware.shtml index 340406e4ad1..3d8269bd3d5 100644 --- a/help/en/parts/SidebarSupportedHardware.shtml +++ b/help/en/parts/SidebarSupportedHardware.shtml @@ -6,6 +6,7 @@
  • Atlas Commander
  • Bachrus +
  • CAN Bus Networks
  • C/MRI
  • CTI Electronics (Acela)
  • CVP EasyDCC @@ -41,6 +42,8 @@
  • Uhlenbrock Intellibox
  • Viessmann Commander
  • Wangrow System One +
  • WiFi Throttles +
  • X10
  • Zimo MX-1
  • ZTC diff --git a/help/en/parts/SidebarTail.shtml b/help/en/parts/SidebarTail.shtml new file mode 100644 index 00000000000..a31c4240271 --- /dev/null +++ b/help/en/parts/SidebarTail.shtml @@ -0,0 +1,17 @@ + +
    +

    Help Table of Contents

    +

    Help Index

    + + + Donate to JMRI + + +
    + + + + + + + diff --git a/help/en/releasenotes/current-draft-note.shtml b/help/en/releasenotes/current-draft-note.shtml index bffcc705ccb..3a606f24a80 100644 --- a/help/en/releasenotes/current-draft-note.shtml +++ b/help/en/releasenotes/current-draft-note.shtml @@ -463,7 +463,7 @@

    Web Access

    WiThrottle Server

    @@ -475,12 +475,17 @@

    Virtual Sound Decoder

    Miscellaneous

    diff --git a/java/src/jmri/implementation/MatrixSignalMast.java b/java/src/jmri/implementation/MatrixSignalMast.java index 7cc6e80b3eb..ac04a880702 100644 --- a/java/src/jmri/implementation/MatrixSignalMast.java +++ b/java/src/jmri/implementation/MatrixSignalMast.java @@ -149,6 +149,7 @@ public void setAspect(@Nonnull String aspect) { } } } + // add a timer here to wait a while before setting new aspect? if (aspectToOutput.containsKey(aspect) && aspectToOutput.get(aspect) != errorBits) { // ToDo: pick up drop down choice for either DCC direct packets or Turnouts as outputs // c.sendPacket(NmraPacket.altAccSignalDecoderPkt(dccSignalDecoderAddress, aspectToOutput.get(aspect)), packetRepeatCount); diff --git a/java/src/jmri/implementation/configurexml/MatrixSignalMastXml.java b/java/src/jmri/implementation/configurexml/MatrixSignalMastXml.java index d6f523a782d..7855b915dc8 100644 --- a/java/src/jmri/implementation/configurexml/MatrixSignalMastXml.java +++ b/java/src/jmri/implementation/configurexml/MatrixSignalMastXml.java @@ -103,6 +103,9 @@ public Element store(Object o) { // from mast p to XML e.addContent(el); } } + if (p.resetPreviousStates()) { + e.addContent(new Element("resetPreviousStates").addContent("yes")); + } return e; } @@ -170,6 +173,12 @@ public boolean load(Element shared, Element perNode) { // from XML to mast m m.setAspectDisabled(asp.getText()); } } + + if ((shared.getChild("resetPreviousStates") != null) // load mast-specific delay, since 4.19.4 + && shared.getChild("resetPreviousStates").getText().equals("yes")) { + m.resetPreviousStates(true); + } + return true; } diff --git a/java/src/jmri/jmrit/beantable/LRouteTableAction.java b/java/src/jmri/jmrit/beantable/LRouteTableAction.java index db78a98a212..5595628e780 100644 --- a/java/src/jmri/jmrit/beantable/LRouteTableAction.java +++ b/java/src/jmri/jmrit/beantable/LRouteTableAction.java @@ -325,6 +325,7 @@ protected String helpTarget() { JTextField _userName = new JTextField(25); JmriJFrame _addFrame = null; + JTabbedPane _tabbedPane = null; RouteInputModel _inputModel; JScrollPane _inputScrollPane; @@ -869,6 +870,7 @@ void cancelPressed(ActionEvent e) { @Override protected void addPressed(ActionEvent e) { makeEditWindow(); + _tabbedPane.setSelectedIndex(0); createButton.setVisible(true); cancelButton.setVisible(true); _typePanel.setVisible(true); @@ -888,7 +890,7 @@ void makeEditWindow() { _addFrame.addHelpMenu("package.jmri.jmrit.beantable.LRouteAddEdit", true); _addFrame.setLocation(100, 30); - JTabbedPane tabbedPane = new JTabbedPane(); + _tabbedPane = new JTabbedPane(); //////////////////////////////////// Tab 1 ///////////////////////////// JPanel tab1 = new JPanel(); @@ -977,7 +979,7 @@ void makeEditWindow() { tab1.add(pb); tab1.setVisible(true); - tabbedPane.addTab(rbx.getString("BasicTab"), null, tab1, rbx.getString("BasicTabHint")); + _tabbedPane.addTab(rbx.getString("BasicTab"), null, tab1, rbx.getString("BasicTabHint")); //////////////////////////////////// Tab 2 ///////////////////////////// JPanel tab2 = new JPanel(); @@ -1008,7 +1010,7 @@ void makeEditWindow() { _outputScrollPane = makeColumns(routeOutputTable, _setStateCombo, true); tab2.add(_outputScrollPane, BorderLayout.CENTER); tab2.setVisible(true); - tabbedPane.addTab(rbx.getString("ActionTab"), null, tab2, rbx.getString("ActionTabHint")); + _tabbedPane.addTab(rbx.getString("ActionTab"), null, tab2, rbx.getString("ActionTabHint")); //////////////////////////////////// Tab 3 ///////////////////////////// JPanel tab3 = new JPanel(); @@ -1040,7 +1042,7 @@ void makeEditWindow() { _inputScrollPane = makeColumns(routeInputTable, _testStateCombo, true); tab3.add(_inputScrollPane, BorderLayout.CENTER); tab3.setVisible(true); - tabbedPane.addTab(rbx.getString("TriggerTab"), null, tab3, rbx.getString("TriggerTabHint")); + _tabbedPane.addTab(rbx.getString("TriggerTab"), null, tab3, rbx.getString("TriggerTabHint")); ////////////////////// Tab 4 ///////////////// JPanel tab4 = new JPanel(); @@ -1113,14 +1115,14 @@ void makeEditWindow() { } tab4.add(alignScrollPane, BorderLayout.CENTER); tab4.setVisible(true); - tabbedPane.addTab(rbx.getString("MiscTab"), null, tab4, rbx.getString("MiscTabHint")); + _tabbedPane.addTab(rbx.getString("MiscTab"), null, tab4, rbx.getString("MiscTabHint")); Container contentPane = _addFrame.getContentPane(); //tabbedPane.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); /////////////////////////////////// JPanel pt = new JPanel(); - pt.add(tabbedPane); + pt.add(_tabbedPane); contentPane.add(pt); // set listener for window closing diff --git a/java/src/jmri/jmrit/cabsignals/CabSignalBundle.properties b/java/src/jmri/jmrit/cabsignals/CabSignalBundle.properties index 7c05ef86951..50aba2cf73c 100644 --- a/java/src/jmri/jmrit/cabsignals/CabSignalBundle.properties +++ b/java/src/jmri/jmrit/cabsignals/CabSignalBundle.properties @@ -33,6 +33,7 @@ NextSignal = Next Signal NextSignalTip = Next signal found NextAspect = Next Aspect NextAspectTip = Aspect of next signal +NextIconTip = Icon of next signal SigDataOn = Active Cab Signal CabsigCheckboxTip = Data send will also be paused by the master Pause / Resume button @@ -43,3 +44,5 @@ ChngDirection = Chng Direction # menu items AspectIconMenu = Aspect Icon IconDegrees = {0} deg +DisplayMenu = Display Options +RowHeightOption = Set Row Height diff --git a/java/src/jmri/jmrit/cabsignals/CabSignalPane.java b/java/src/jmri/jmrit/cabsignals/CabSignalPane.java index e3ae0e17b9b..6854e061f65 100644 --- a/java/src/jmri/jmrit/cabsignals/CabSignalPane.java +++ b/java/src/jmri/jmrit/cabsignals/CabSignalPane.java @@ -12,21 +12,13 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import javax.swing.BoxLayout; -import javax.swing.ButtonGroup; -import javax.swing.JButton; -import javax.swing.JCheckBoxMenuItem; -import javax.swing.JLabel; -import javax.swing.JMenu; -import javax.swing.JPanel; -import javax.swing.JRadioButtonMenuItem; -import javax.swing.JScrollPane; -import javax.swing.JTable; -import javax.swing.JToggleButton; +import javax.swing.*; +import javax.swing.event.ChangeEvent; import javax.swing.table.JTableHeader; import javax.swing.table.TableCellRenderer; import javax.swing.table.TableColumn; import javax.swing.table.TableRowSorter; +import javax.swing.text.DefaultFormatter; import jmri.LocoAddress; import jmri.CabSignalListListener; import jmri.CabSignalManager; @@ -35,6 +27,7 @@ import jmri.jmrit.roster.swing.GlobalRosterEntryComboBox; import jmri.jmrit.roster.swing.RosterEntryComboBox; import jmri.util.swing.XTableColumnModel; +import jmri.util.swing.StayOpenCheckBoxItem; import jmri.util.table.ButtonEditor; import jmri.util.table.ButtonRenderer; @@ -52,35 +45,57 @@ public class CabSignalPane extends jmri.util.swing.JmriPanel implements CabSigna private CabSignalManager cabSignalManager; - protected JScrollPane slotScroll; - - protected CabSignalTableModel slotModel=null; - protected JTable slotTable=null; - protected final XTableColumnModel tcm = new XTableColumnModel(); + private JScrollPane slotScroll; + private CabSignalTableModel slotModel; + private JTable _slotTable; + private XTableColumnModel tcm; - private JMenu cabSigColMenu = new JMenu(Bundle.getMessage("SigDataCol")); - - protected List colMenuList = new ArrayList(); - protected List cabSigColMenuList = new ArrayList(); + private JMenu cabSigColMenu; + private List colMenuList; private JToggleButton masterPauseButton; - JLabel textLocoLabel = new JLabel(); - DccLocoAddressSelector locoSelector = new DccLocoAddressSelector(); - RosterEntryComboBox locoRosterBox; - JButton addLocoButton = new JButton(); - JButton resetLocoButton = new JButton(); + private JLabel textLocoLabel; + private DccLocoAddressSelector locoSelector; + private RosterEntryComboBox locoRosterBox; + private JButton addLocoButton; + private JButton resetLocoButton; private int _rotationOffset; + private int _defaultRowHeight; + public CabSignalPane() { + super(); + cabSignalManager = jmri.InstanceManager.getNullableDefault(CabSignalManager.class); + if(cabSignalManager == null){ + log.info("creating new DefaultCabSignalManager"); + jmri.InstanceManager.store(new jmri.managers.DefaultCabSignalManager(),CabSignalManager.class); + cabSignalManager = jmri.InstanceManager.getNullableDefault(CabSignalManager.class); + } + } + + /** + * {@inheritDoc} + */ @Override public void initComponents() { super.initComponents(); + if (cabSignalManager != null) { + cabSignalManager.addCabSignalListListener(this); + } slotModel = new CabSignalTableModel(5, CabSignalTableModel.MAX_COLUMN); // row, column + + tcm = new XTableColumnModel(); + cabSigColMenu = new JMenu(Bundle.getMessage("SigDataCol")); + colMenuList = new ArrayList<>(); + textLocoLabel = new JLabel(); + locoSelector = new DccLocoAddressSelector(); + addLocoButton = new JButton(); + resetLocoButton = new JButton(); + _defaultRowHeight = 26; init(); } - public void init() { - JTable slotTable = new JTable(slotModel) { + _slotTable = new JTable(slotModel) { // Override JTable Header to implement table header tool tips. @Override protected JTableHeader createDefaultTableHeader() { @@ -91,7 +106,7 @@ public String getToolTipText(MouseEvent e) { java.awt.Point p = e.getPoint(); int index = columnModel.getColumnIndexAtX(p.x); int realIndex = columnModel.getColumn(index).getModelIndex(); - return CabSignalTableModel.columnToolTips[realIndex]; + return CabSignalTableModel.COLUMNTOOLTIPS[realIndex]; } catch (RuntimeException e1) { //catch null pointer exception if mouse is over an empty line } @@ -102,13 +117,14 @@ public String getToolTipText(MouseEvent e) { }; // Use XTableColumnModel so we can control which columns are visible - slotTable.setColumnModel(tcm); - slotTable.createDefaultColumnsFromModel(); + _slotTable.setColumnModel(tcm); + _slotTable.createDefaultColumnsFromModel(); - for (int i = 0; i < slotTable.getColumnCount(); i++) { + for (int i = 0; i < _slotTable.getColumnCount(); i++) { int colnumber=i; - String colName = slotTable.getColumnName(colnumber); - JCheckBoxMenuItem showcol = new JCheckBoxMenuItem(colName); + String colName = _slotTable.getColumnName(colnumber); + StayOpenCheckBoxItem showcol = new StayOpenCheckBoxItem(colName); + showcol.setToolTipText(CabSignalTableModel.COLUMNTOOLTIPS[i]); colMenuList.add(showcol); cabSigColMenu.add(showcol); // cabsig columns } @@ -117,7 +133,7 @@ public String getToolTipText(MouseEvent e) { int colnumber=i; TableColumn column = tcm.getColumnByModelIndex(colnumber); - if (Arrays.stream(CabSignalTableModel.startupColumns).anyMatch(j -> j == colnumber)) { + if (Arrays.stream(CabSignalTableModel.STARTUPCOLUMNS).anyMatch(j -> j == colnumber)) { colMenuList.get(colnumber).setSelected(true); tcm.setColumnVisible(column, true); } else { @@ -125,25 +141,22 @@ public String getToolTipText(MouseEvent e) { tcm.setColumnVisible(column, false); } - colMenuList.get(colnumber).addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent e) { - TableColumn column = tcm.getColumnByModelIndex(colnumber); - boolean visible = tcm.isColumnVisible(column); - tcm.setColumnVisible(column, !visible); - } + colMenuList.get(colnumber).addActionListener((ActionEvent e) -> { + TableColumn column1 = tcm.getColumnByModelIndex(colnumber); + boolean visible1 = tcm.isColumnVisible(column1); + tcm.setColumnVisible(column1, !visible1); }); } - slotTable.setAutoCreateRowSorter(true); + _slotTable.setAutoCreateRowSorter(true); - final TableRowSorter sorter = new TableRowSorter(slotModel); - slotTable.setRowSorter(sorter); + final TableRowSorter sorter = new TableRowSorter<>(slotModel); + _slotTable.setRowSorter(sorter); - slotTable.setRowHeight(26); + _slotTable.setRowHeight(_defaultRowHeight); // configure items for GUI - slotModel.configureTable(slotTable); + slotModel.configureTable(_slotTable); tcm.getColumnByModelIndex(CabSignalTableModel.REVERSE_BLOCK_DIR_BUTTON_COLUMN).setCellRenderer( new ButtonRenderer() ); @@ -153,7 +166,7 @@ public void actionPerformed(ActionEvent e) { tcm.getColumnByModelIndex(CabSignalTableModel.NEXT_ASPECT_ICON).setCellRenderer( tableSignalAspectRenderer() ); - slotScroll = new JScrollPane(slotTable); + slotScroll = new JScrollPane(_slotTable); slotScroll.setPreferredSize(new Dimension(400, 200)); this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); @@ -169,11 +182,8 @@ public void actionPerformed(ActionEvent e) { masterPauseButton.setSelected(false); // cabdata on refreshMasterPauseButton(); masterPauseButton.setVisible(true); - masterPauseButton.addActionListener (new ActionListener () { - @Override - public void actionPerformed(ActionEvent e) { - refreshMasterPauseButton(); - } + masterPauseButton.addActionListener ((ActionEvent e) -> { + refreshMasterPauseButton(); }); toppanelcontainer.add(masterPauseButton); @@ -262,33 +272,25 @@ private void refreshMasterPauseButton(){ } } + /** + * {@inheritDoc} + */ @Override public String getTitle() { return Bundle.getMessage("CabSignalPaneTitle"); } - - public CabSignalPane() { - super(); - cabSignalManager = jmri.InstanceManager.getNullableDefault(CabSignalManager.class); - if(cabSignalManager == null){ - log.info("creating new DefaultCabSignalManager"); - jmri.InstanceManager.store(new jmri.managers.DefaultCabSignalManager(),CabSignalManager.class); - cabSignalManager = jmri.InstanceManager.getNullableDefault(CabSignalManager.class); - } - if (cabSignalManager != null) { - cabSignalManager.addCabSignalListListener(this); - } - } /** - * Creates a Menu List + * {@inheritDoc} */ @Override public List getMenus() { - List menuList = new ArrayList(); + List menuList = new ArrayList<>(); menuList.add(cabSigColMenu); + JMenu displayMenu = new JMenu(Bundle.getMessage("DisplayMenu")); + JMenu iconMenu = new JMenu(Bundle.getMessage("AspectIconMenu")); ButtonGroup offsetGroup = new ButtonGroup(); @@ -307,7 +309,7 @@ public List getMenus() { iconMenu.add(offset2MenuItem); iconMenu.add(offset3MenuItem); - menuList.add(iconMenu); + displayMenu.add(iconMenu); _rotationOffset = 0; // startup offset0MenuItem.setSelected(true); @@ -331,8 +333,41 @@ else if ( offset3MenuItem.isSelected() ) { offset2MenuItem.addActionListener(iconMenuListener); offset3MenuItem.addActionListener(iconMenuListener); + ActionListener rowHeightMenuListener = ae -> { + JSpinner delaySpinner = getNewRowHeightSpinner(); + int option = JOptionPane.showOptionDialog(this, + delaySpinner, + Bundle.getMessage("RowHeightOption"), + JOptionPane.OK_CANCEL_OPTION, + JOptionPane.QUESTION_MESSAGE, null, null, null); + if (option == JOptionPane.OK_OPTION) { + _defaultRowHeight = (Integer) delaySpinner.getValue(); + } + else { + _slotTable.setRowHeight(_defaultRowHeight); + } + }; + + JMenuItem searchForNodesMenuItem = new JMenuItem(Bundle.getMessage("RowHeightOption")); + searchForNodesMenuItem.addActionListener(rowHeightMenuListener); + displayMenu.add(searchForNodesMenuItem); + + menuList.add(displayMenu); + return menuList; } + + private JSpinner getNewRowHeightSpinner() { + JSpinner rqnnSpinner = new JSpinner(new SpinnerNumberModel(_defaultRowHeight, 10, 150, 1)); + JComponent rqcomp = rqnnSpinner.getEditor(); + JFormattedTextField rqfield = (JFormattedTextField) rqcomp.getComponent(0); + DefaultFormatter rqformatter = (DefaultFormatter) rqfield.getFormatter(); + rqformatter.setCommitsOnValidEdit(true); + rqnnSpinner.addChangeListener((ChangeEvent e) -> { + _slotTable.setRowHeight((Integer) rqnnSpinner.getValue()); + }); + return rqnnSpinner; + } public void addLocoButtonActionPerformed(ActionEvent e) { if (locoSelector.getAddress() == null) { @@ -349,11 +384,13 @@ public void locoSelected() { } } - private TableCellRenderer tableSignalAspectRenderer() { return new TableCellRenderer() { JLabel f = new JLabel(); + /** + * {@inheritDoc} + */ @Override public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) { @@ -385,17 +422,22 @@ public String getHelpTarget() { return "package.jmri.jmrit.cabsignals.CabSignalPane"; } + /** + * {@inheritDoc} + */ @Override public void dispose() { cabSignalManager.removeCabSignalListListener(this); - slotTable = null; + _slotTable = null; slotModel.dispose(); cabSignalManager = null; super.dispose(); } - // Cab Signal List Listener interface - + /** + * {@inheritDoc} + * Cab Signal List Listener interface + */ @Override public void notifyCabSignalListChanged(){ slotModel.fireTableDataChanged(); diff --git a/java/src/jmri/jmrit/cabsignals/CabSignalTableModel.java b/java/src/jmri/jmrit/cabsignals/CabSignalTableModel.java index dbe6b0b3722..f07b4ce7f7d 100644 --- a/java/src/jmri/jmrit/cabsignals/CabSignalTableModel.java +++ b/java/src/jmri/jmrit/cabsignals/CabSignalTableModel.java @@ -41,7 +41,7 @@ public class CabSignalTableModel extends javax.swing.table.AbstractTableModel { static public final int MAX_COLUMN = 9; - static protected final int[] startupColumns = {0,1,2,3,4,5,6,7,8}; + static protected final int[] STARTUPCOLUMNS = {0,1,2,3,4,5,6,7,8}; CabSignalTableModel(int row, int column) { cabSignalManager = InstanceManager.getNullableDefault(CabSignalManager.class); @@ -53,7 +53,7 @@ public class CabSignalTableModel extends javax.swing.table.AbstractTableModel { } // order needs to match column list top of dtabledatamodel - static protected final String[] columnToolTips = { + static protected final String[] COLUMNTOOLTIPS = { null, // loco id Bundle.getMessage("CabsigCheckboxTip"), Bundle.getMessage("BlockUserName"), @@ -62,18 +62,21 @@ public class CabSignalTableModel extends javax.swing.table.AbstractTableModel { Bundle.getMessage("NextBlockTip"), Bundle.getMessage("NextSignalTip"), Bundle.getMessage("NextAspectTip"), - Bundle.getMessage("NextAspectTip"), // aspect icon + Bundle.getMessage("NextIconTip"), // aspect icon }; // Length = number of items in array should (at least) match number of columns /** - * Return the number of rows to be displayed. + * {@inheritDoc} */ @Override public int getRowCount() { return cabSignalManager.getCabSignalList().size(); } + /** + * {@inheritDoc} + */ @Override public int getColumnCount() { return MAX_COLUMN; @@ -105,7 +108,7 @@ public String getColumnName(int col) { // not in any order case SEND_CABSIG_COLUMN: return Bundle.getMessage("SigDataOn"); case NEXT_ASPECT_ICON: - return Bundle.getMessage("NextAspect"); + return Bundle.getMessage("AspectIconMenu"); default: return "unknown"; // NOI18N } @@ -115,27 +118,24 @@ public String getColumnName(int col) { // not in any order * Returns int of startup column widths. * * @param col int col number + * @return initial preferred width */ public static int getPreferredWidth(int col) { switch (col) { + case SEND_CABSIG_COLUMN: + case NEXT_ASPECT_ICON: + return new JTextField(3).getPreferredSize().width; case LOCO_ID_COLUMN: return new JTextField(4).getPreferredSize().width; - case CURRENT_BLOCK: - return new JTextField(8).getPreferredSize().width; case BLOCK_DIR: + case NEXT_SIGNAL: return new JTextField(6).getPreferredSize().width; - case REVERSE_BLOCK_DIR_BUTTON_COLUMN: - return new JTextField(10).getPreferredSize().width; + case CURRENT_BLOCK: case NEXT_BLOCK: return new JTextField(8).getPreferredSize().width; - case NEXT_SIGNAL: - return new JTextField(6).getPreferredSize().width; + case REVERSE_BLOCK_DIR_BUTTON_COLUMN: case NEXT_ASPECT: return new JTextField(10).getPreferredSize().width; - case SEND_CABSIG_COLUMN: - return new JTextField(3).getPreferredSize().width; - case NEXT_ASPECT_ICON: - return new JTextField(3).getPreferredSize().width; default: log.warn("no width found col {}",col); return new JTextField(" ").getPreferredSize().width; // NOI18N @@ -143,29 +143,24 @@ public static int getPreferredWidth(int col) { } /** - * Returns column class type. + * {@inheritDoc} */ @Override public Class getColumnClass(int col) { switch (col) { case LOCO_ID_COLUMN: return LocoAddress.class; - case CURRENT_BLOCK: - return String.class; - case BLOCK_DIR: - return String.class; case REVERSE_BLOCK_DIR_BUTTON_COLUMN: return javax.swing.JButton.class; + case CURRENT_BLOCK: + case BLOCK_DIR: case NEXT_BLOCK: - return String.class; case NEXT_SIGNAL: - return String.class; case NEXT_ASPECT: + case NEXT_ASPECT_ICON: return String.class; case SEND_CABSIG_COLUMN: return Boolean.class; - case NEXT_ASPECT_ICON: - return String.class; default: log.error("no column class located"); return null; @@ -173,9 +168,7 @@ public Class getColumnClass(int col) { } /** - * Boolean return to edit table cell or not. - * - * @return boolean + * {@inheritDoc} */ @Override public boolean isCellEditable(int row, int col) { @@ -193,6 +186,7 @@ public boolean isCellEditable(int row, int col) { *

    * This is optional, in that other table formats can use this table model. * But we put it here to help keep it consistent. + * @param cmdStatTable Table to be configured */ public void configureTable(JTable cmdStatTable) { // allow reordering of the columns @@ -210,10 +204,7 @@ public void configureTable(JTable cmdStatTable) { } /** - * Return table values. - * - * @param row int row number - * @param col int col number + * {@inheritDoc} */ @Override public Object getValueAt(int row, int col) { @@ -276,9 +267,6 @@ public Object getValueAt(int row, int col) { // should start at the resources directory return newlink; } - else { - return ""; - } } return ""; case SEND_CABSIG_COLUMN: @@ -290,35 +278,27 @@ public Object getValueAt(int row, int col) { } /** - * @param value object value - * @param row int row number - * @param col int col number + * {@inheritDoc} */ @Override public void setValueAt(Object value, int row, int col) { - if (col == LOCO_ID_COLUMN) { - } - else if (col == CURRENT_BLOCK) { - } - else if (col == BLOCK_DIR) { - } - else if (col == REVERSE_BLOCK_DIR_BUTTON_COLUMN) { - cabSignalManager.getCabSignalArray()[row].setBlock(); - chngblockdir(row); - } - else if (col == NEXT_BLOCK) { - jmri.util.ThreadingUtil.runOnLayout( ()->{ - BlockManager bmgr = jmri.InstanceManager.getDefault(jmri.BlockManager.class); - Block b = bmgr.getBlock((String)value); - cabSignalManager.getCabSignalArray()[row].setBlock(b); - }); - } - else if (col == NEXT_SIGNAL) { - } - else if (col == NEXT_ASPECT) { - } - else if (col == SEND_CABSIG_COLUMN) { - cabSignalManager.getCabSignalArray()[row].setCabSignalActive((Boolean) value); + switch (col) { + case REVERSE_BLOCK_DIR_BUTTON_COLUMN: + cabSignalManager.getCabSignalArray()[row].setBlock(); + chngblockdir(row); + break; + case NEXT_BLOCK: + jmri.util.ThreadingUtil.runOnLayout( ()->{ + BlockManager bmgr = jmri.InstanceManager.getDefault(jmri.BlockManager.class); + Block b = bmgr.getBlock((String)value); + cabSignalManager.getCabSignalArray()[row].setBlock(b); + }); + break; + case SEND_CABSIG_COLUMN: + cabSignalManager.getCabSignalArray()[row].setCabSignalActive((Boolean) value); + break; + default: + break; } } @@ -329,7 +309,7 @@ else if (col == SEND_CABSIG_COLUMN) { */ private void chngblockdir(int row){ log.debug("changing block direction for row {}", row); - int olddirection = 0; + int olddirection; Block b = cabSignalManager.getCabSignalArray()[row].getBlock(); if (b == null){ cabSignalManager.getCabSignalArray()[row].setBlock(); diff --git a/java/test/jmri/implementation/MatrixSignalMastTest.java b/java/test/jmri/implementation/MatrixSignalMastTest.java index f9d27241b5c..9ecfdae99c3 100644 --- a/java/test/jmri/implementation/MatrixSignalMastTest.java +++ b/java/test/jmri/implementation/MatrixSignalMastTest.java @@ -157,6 +157,7 @@ public void testAspects() { m.setAspectEnabled("Approach"); m.setAspectEnabled("Stop"); m.setAspectEnabled("Unlit"); + m.resetPreviousStates(false); m.aspect = "Stop"; // define some initial aspect before setting any aspect m.setMatrixMastCommandDelay(0); diff --git a/scripts/HOWTO-distribution.md b/scripts/HOWTO-distribution.md index e3868012767..4b4a019bbb7 100644 --- a/scripts/HOWTO-distribution.md +++ b/scripts/HOWTO-distribution.md @@ -547,7 +547,7 @@ git push github Test version 4.19.2 of JMRI/DecoderPro is available for download. -This is the next in a series of test releases that will culminate in a production release, hopefully in early December 2019. +This is the next in a series of test releases that will culminate in a production release, hopefully in early summer 2020. - Alt: There have been a lot of updates in this version, so it should be considered experimental. - Alt: We're getting close to the end of the development series, so we'd appreciate feedback on whether or not this release works for your layout. diff --git a/web/js/jquery.jmri.js b/web/js/jquery.jmri.js index 73e40cd7406..8d56d9b003f 100644 --- a/web/js/jquery.jmri.js +++ b/web/js/jquery.jmri.js @@ -950,8 +950,8 @@ var m = JSON.parse(e.originalEvent.data); if ($.isArray(m)) { - m.forEach(o => { - h = jmri.events[o.type]; + m.forEach(function(o) { + var h = jmri.events[o.type]; if (h) { h.call(this, o); } else if (!o.type) { @@ -961,7 +961,7 @@ } }) } else { - h = jmri.events[m.type]; + var h = jmri.events[m.type]; if (h) { h.call(this, m); } else if (!m.type) { diff --git a/web/js/panel.js b/web/js/panel.js index 544794ff9be..e475ad9e7ce 100644 --- a/web/js/panel.js +++ b/web/js/panel.js @@ -101,8 +101,8 @@ var jmri_logging = false; // // log object properties -function $logProperties(obj, force = false) { - if (jmri_logging || force) { +function $logProperties(obj) { + if (jmri_logging) { var $propList = ""; for (var $propName in obj) { if (typeof obj[$propName] !== "undefined") { @@ -128,7 +128,7 @@ function isDefined(x) { class Decoration { constructor($widget) { //jmri.log("Decoration.constructor(...)"); - //$logProperties(this.$widget, true); + $logProperties(this.$widget); this.$widget = $widget; } getEndPoints() { @@ -1450,7 +1450,7 @@ function processPanelXML($returnedData, $success, $xhr) { //store these blocks in a persistent var $gBlks[$widget.systemName] = $widget; //jmri.log("layoutblock:"); - //$logProperties($widget, true); + $logProperties($widget); //jmri.log("block[" + $widget.systemName + "].blockcolor: '" + $widget.trackcolor + "'.") jmri.getLayoutBlock($widget.systemName); break; @@ -1755,7 +1755,7 @@ function processPanelXML($returnedData, $success, $xhr) { //loop thru raytracks, calc and store end of ray point for each $widget['raytracks'] = $(this).find('raytrack'); $widget.raytracks.each(function(i, item) { - //$logProperties(item, true); + $logProperties(item); //note:the 50 offset is due to TrackSegment.java TURNTABLE_RAY_OFFSET var rayID = $widget.ident + "." + (50 + item.attributes.index.value * 1); var $t = {ident:rayID}; @@ -1935,7 +1935,7 @@ function $handleClick(e) { var $turntableID = $rayID.split(".")[0]; var $widget = $gWidgets[$turntableID]; $widget.raytracks.each(function(i, item) { - //$logProperties(item, true); + $logProperties(item); //note:offset 50 is due to TrackSegment.java TURNTABLE_RAY_OFFSET var rayID = $turntableID + "." + (50 + item.attributes.index.value * 1); if (rayID == $rayID) { @@ -2045,7 +2045,6 @@ function $drawTrackSegment($widget) { if ($widget.hidden == "yes") { return; } - //$logProperties($widget, true); //TODO: remove or comment out for production // if positional points have not been loaded... if (Object.keys($gPts).length == 0) { @@ -2064,11 +2063,6 @@ function $drawTrackSegment($widget) { return; } - // if ($widget.ident == "T5") { - // //jmri.log("$widget.ident:" + $widget.ident); - // $logProperties($widget, true); - // } - $gCtx.save(); // save current line width and color //set trackcolor based on blockcolor @@ -2128,7 +2122,7 @@ function $drawTrackSegmentBezier($widget) { //$point_log("points[0]", points[0]); - $drawBezier(points); + $drawBezier(points, $gCtx.strokeStyle, $gCtx.lineWidth, 0); } function $drawTrackSegmentCircle($widget) { @@ -2278,7 +2272,7 @@ function $drawIcon($widget) { //draw a turntable (pass in widget) //from jmri.jmrit.display.layoutEditor.layoutTurntable function $drawTurntable($widget) { - //$logProperties($widget, true); + $logProperties($widget); //get the center var $txcen = $widget.xcen * 1; @@ -2294,7 +2288,7 @@ function $drawTurntable($widget) { //loop thru raytracks drawing each one (and control circles if it has a turnout) $widget.raytracks.each(function(i, item) { - //$logProperties(item, true); + $logProperties(item); var rayID = $widget.ident + "." + (50 + item.attributes.index.value * 1); var $t = $gPts[rayID]; //draw the line from ray endpoint to turntable edge @@ -3142,16 +3136,11 @@ function $drawEllipse(x, y, rw, rh, startAngleRAD, stopAngleRAD) // $drawBezier // var bezier1st = true; -function $drawBezier(points, $color, $width, displacement = 0) { +function $drawBezier(points, $color, $width, displacement) { $gCtx.save(); // save current line width and color - // set color and width - if (typeof $color !== "undefined") { - $gCtx.strokeStyle = $color; - } - if (typeof $width !== "undefined") { - $gCtx.lineWidth = $width; - } + $gCtx.strokeStyle = $color; + $gCtx.lineWidth = $width; try { bezier1st = true; @@ -3176,7 +3165,7 @@ function $drawBezier(points, $color, $width, displacement = 0) { // //plotBezier - recursive function to draw bezier curve // -function $plotBezier(points, depth = 0, displacement = 0) { +function $plotBezier(points, depth, displacement) { var len = points.length, idx, jdx; // calculate flatness to determine if we need to recurse... @@ -3274,7 +3263,7 @@ function $point_midpoint(p1, p2) { return [$half(p1[0], p2[0]), $half(p1[1], p2[1])]; } -function $point_normalizeTo(p, new_length = 1) { +function $point_normalizeTo(p, new_length) { var m = new_length / $point_length(p); return [p[0] * m, p[1] * m]; } @@ -3568,7 +3557,7 @@ var $setWidgetState = function($id, $newState, data) { $id = slipID; } else if ($id.startsWith("TUR")) { //jmri.log("$setWidgetState(" + $id + ", " + $newState + ", " + data + ")"); - //$logProperties(data, true); + $logProperties(data); var turntableID = $id.split(".")[0]; $widget = $gWidgets[turntableID]; @@ -3702,7 +3691,7 @@ jQuery.fn.xmlClean = function() { //handle the toggling (or whatever) of the "next" state for the passed-in widget var $getNextState = function($widget) { var $nextState = undefined; - //$logProperties($widget); + $logProperties($widget); if ($widget.widgetType == 'signalheadicon') { //special case for signalheadicons switch ($widget.clickmode * 1) { // logic based on SignalHeadIcon.java case 0 : @@ -3878,7 +3867,7 @@ function $redrawBlock(blockName) { //loop thru widgets, if block matches, redraw widget by proper method jQuery.each($gWidgets, function($id, $widget) { ///jmri.log(" $id: " + $id); - //$logProperties($widget); + $logProperties($widget); if (($widget.blockname == blockName) || ($widget.blocknameac == blockName) || ($widget.blocknamebd == blockName) @@ -4014,7 +4003,7 @@ function updateBlockSensorState(blockName, sensorName, sensorState) { // jmri.log("blockName: " + blockName // + ", sensorName: " + sensorName // + ", sensorState: " + sensorState); -// $logProperties($blk, true); +// $logProperties($blk); if (isDefined($blk.occupancysensor) && ($blk.occupancysensor == sensorName)) { $blk.state = sensorState; diff --git a/xml/schema/types/signalmasts-2-9-6.xsd b/xml/schema/types/signalmasts-2-9-6.xsd index f2210112c7e..c65d675542d 100644 --- a/xml/schema/types/signalmasts-2-9-6.xsd +++ b/xml/schema/types/signalmasts-2-9-6.xsd @@ -467,6 +467,13 @@ + + + + "yes" means that the dark turnout command should be sent before each new aspects turnout command + + +