From 73a3aaa5380df49d680f4f96d5978ff309eb487b Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Tue, 19 Nov 2024 12:15:33 -0500 Subject: [PATCH 01/15] new hints and visibility elements for groups --- sample.xml | 75 +++++++++++++++++-- src/org/openlcb/cdi/CdiRep.java | 2 + .../cdi/impl/ConfigRepresentation.java | 9 +++ src/org/openlcb/cdi/jdom/JdomCdiRep.java | 33 ++++++++ src/org/openlcb/cdi/swing/CdiPanel.java | 6 +- 5 files changed, 117 insertions(+), 8 deletions(-) diff --git a/sample.xml b/sample.xml index dc327a8f..6c03b46f 100644 --- a/sample.xml +++ b/sample.xml @@ -17,23 +17,50 @@ Produced Events The EventIDs for the producers + + Consumed Events The EventIDs for the consumers + + Blob to see if works in group element - + Float to see if works in group element Int of size 4 so that each group is 32 long + + Hideable and Hidden Nested Group + + + + + + + Hideable and Not Hidden Nested Group + + + + + + + Non-hideable Nested Group + + + Sample integer variable @@ -42,14 +69,28 @@ 999 12 + + Sample float variable + Doesn't do anything + 1 + 999 + 12 + + + Same float variable + Overlaps previous + 0 + 10000 + 12 + Sample integer slider Doesn't do anything either - 0 - 1000 + 1 + 250 12 - + @@ -59,11 +100,35 @@ 1000 12 - + + + Sample Single Group + \n\ + + + \n\ + Output On Period\n\ + \n\ + \n\ + Output Off Period\n\ + \n\ + \n\ + Units\n\ + "xstr(UNITS_DEFAULT)"\n\ + \n\ + "xstr(UNITS_MS)"Milliseconds (ms)\n\ + "xstr(UNITS_S)"Seconds (s)\n\ + "xstr(UNITS_M)"Minutes (m)\n\ + \n\ + \n\ + \n\ + + Reset via Map diff --git a/src/org/openlcb/cdi/CdiRep.java b/src/org/openlcb/cdi/CdiRep.java index e751e541..fbc5cde5 100644 --- a/src/org/openlcb/cdi/CdiRep.java +++ b/src/org/openlcb/cdi/CdiRep.java @@ -45,6 +45,8 @@ public static interface Group extends Item { public java.util.List getItems(); public int getReplication(); public String getRepName(int index, int replications); + public boolean isHideable(); + public boolean isHidden(); } public static interface Map { diff --git a/src/org/openlcb/cdi/impl/ConfigRepresentation.java b/src/org/openlcb/cdi/impl/ConfigRepresentation.java index 7dd14342..51a705d9 100644 --- a/src/org/openlcb/cdi/impl/ConfigRepresentation.java +++ b/src/org/openlcb/cdi/impl/ConfigRepresentation.java @@ -574,6 +574,15 @@ public class GroupEntry extends GroupBase { } } } + + public boolean isHideable() { + return group.isHideable(); + } + + public boolean isHidden() { + return group.isHidden(); + } + } /** diff --git a/src/org/openlcb/cdi/jdom/JdomCdiRep.java b/src/org/openlcb/cdi/jdom/JdomCdiRep.java index 261c78ae..509296c1 100644 --- a/src/org/openlcb/cdi/jdom/JdomCdiRep.java +++ b/src/org/openlcb/cdi/jdom/JdomCdiRep.java @@ -6,6 +6,7 @@ import java.util.logging.Logger; import org.jdom2.Attribute; +import org.jdom2.DataConversionException; import org.jdom2.Element; import org.openlcb.cdi.CdiRep; @@ -289,6 +290,38 @@ public int getOffset() { } catch (org.jdom2.DataConversionException e1) { return 0; } } + public boolean isHideable() { + // defaults to false + Element hints = e.getChild("hints"); + if (hints == null) return false; + Element visibility = hints.getChild("visibility"); + if (visibility == null) return false; + Attribute a = visibility.getAttribute("hideable"); + if (a == null) return false; + try { + boolean value = a.getBooleanValue(); + return value; + } catch (DataConversionException ex) { + return false; + } + } + + public boolean isHidden() { + // defaults to false + Element hints = e.getChild("hints"); + if (hints == null) return false; + Element visibility = hints.getChild("visibility"); + if (visibility == null) return false; + Attribute a = visibility.getAttribute("hidden"); + if (a == null) return false; + try { + boolean value = a.getBooleanValue(); + return value; + } catch (DataConversionException ex) { + return false; + } + } + /** * Provides the name for this replication. See the CDI TN for the * algorithm being used. diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index d8291376..7749aa49 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -1101,14 +1101,14 @@ public void visitGroup(ConfigRepresentation.GroupEntry e) { super.visitGroup(e); factory.handleGroupPaneEnd(groupPane); - if (groupPane.getComponentCount() > 0) { - if (oldPane instanceof SegmentPane) { - // we make toplevel groups collapsible. + if (groupPane.getComponentCount() > 0) { // empty groups are not collabsible + if (oldPane instanceof SegmentPane || e.isHideable()) { // we only make toplevel groups collapsible unless hint requests groupPane.setBorder(null); CollapsiblePanel cPanel = new CollapsiblePanel(groupPane.getName(), groupPane); // cPanel.setBorder(BorderFactory.createLineBorder(java.awt.Color.RED)); //debugging cPanel.setAlignmentY(Component.TOP_ALIGNMENT); cPanel.setAlignmentX(Component.LEFT_ALIGNMENT); + cPanel.setExpanded(!e.isHidden()); oldPane.add(cPanel); addNavigationActions(cPanel); } else { From 29e8890f3c44df8fe414518cbbe2ef0a9899c6d6 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Wed, 20 Nov 2024 08:41:23 -0500 Subject: [PATCH 02/15] hints element isn't unexpected --- src/org/openlcb/cdi/jdom/JdomCdiRep.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/openlcb/cdi/jdom/JdomCdiRep.java b/src/org/openlcb/cdi/jdom/JdomCdiRep.java index 509296c1..45b5c283 100644 --- a/src/org/openlcb/cdi/jdom/JdomCdiRep.java +++ b/src/org/openlcb/cdi/jdom/JdomCdiRep.java @@ -128,6 +128,7 @@ public java.util.List getItems() { case "repname": case "name": case "description": + case "hints": break; default: list.add(new UnknownRep(element)); From 61649bb5b4c4e8c0429ce2b4f6096a8e76f19ad4 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Thu, 21 Nov 2024 07:33:41 -0500 Subject: [PATCH 03/15] fix sizing of large text fields --- src/org/openlcb/cdi/swing/CdiPanel.java | 29 ++++++++++++++++++++----- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 7749aa49..04ac73cb 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -1924,15 +1924,31 @@ protected void init() { JPanel combinedPanel = new JPanel(); combinedPanel.setLayout(new BoxLayout(combinedPanel, BoxLayout.Y_AXIS)); - combinedPanel.add(new JScrollPane(textComponent){ + JScrollPane spane = new JScrollPane(textComponent){ // Limit how small the layout will make the field public Dimension getMinimumSize() { Dimension superSize = super.getMinimumSize(); int width = superSize.width; - int height = Math.max(superSize.height, 200); + int height = Math.max(superSize.height, 50); return new Dimension(width, height); } - }); + public Dimension getPreferredSize() { + Dimension superMin = super.getMinimumSize(); + Dimension superPref = super.getPreferredSize(); + int width = Math.max(superMin.width, superPref.width); + int height = Math.max(superMin.height, superPref.height); + return new Dimension(width, height); + } + public Dimension getMaximumSize() { + Dimension superMax = super.getMaximumSize(); + Dimension superPref = super.getPreferredSize(); + int width = Math.max(superMax.width, superPref.width); + int height = Math.max(superMax.height, superPref.height); + return new Dimension(width, height); + } + }; + spane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); + combinedPanel.add(spane); combinedPanel.add(lengthPanel); p3.add(combinedPanel); @@ -2722,7 +2738,10 @@ public Dimension getMaximumSize() { textField = jtf; } else { // Long string. Show multi-line editor - JTextArea jta = new JTextArea(doc, "", Math.min(40, (int)(entry.size / 40)), 80);// line count is heuristic + // For character count handling, see EntryPane#init() below + JTextArea jta = new JTextArea(doc, "", Math.min(40, (int)(entry.size / 32)), 80); + // Line count estimate is heuristic + // Limited to 40 lines to keep GUI under control jta.setEditable(true); jta.setLineWrap(true); jta.setWrapStyleWord(true); @@ -2731,7 +2750,7 @@ public Dimension getMaximumSize() { textField = jta; } textComponent = textField; - textComponent.setToolTipText("String of up to "+entry.size+" characters"); + textComponent.setToolTipText("String of up to "+(entry.size-1)+" characters"); // -1 for terminating zero in field init(); } From d290940fcdd6f2c36069e0355d60acf77a83c7f0 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Thu, 21 Nov 2024 12:35:52 -0500 Subject: [PATCH 04/15] move long-string buttons below field --- src/org/openlcb/cdi/swing/CdiPanel.java | 28 ++++++++++++++++++------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 04ac73cb..4ce0faa8 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -1909,16 +1909,20 @@ void release() { protected void additionalButtons() {} protected void init() { + // a panel that may exist below the main component (textComponent) + // which, if present, gets the Refresh and Write buttons + JPanel subpanel = null; + if (textComponent instanceof JTextArea) { - JPanel lengthPanel = new JPanel(); + subpanel = new JPanel(); // include an auto-updating "remaining characters" field - lengthPanel.setLayout(new FlowLayout()); + subpanel.setLayout(new FlowLayout()); JLabel lengthLabel = new JLabel("Remaining characters: "); - lengthPanel.add(lengthLabel); + subpanel.add(lengthLabel); final JTextField countField = new JTextField(6); countField.setText(""+(entry.size-1)); - lengthPanel.add(countField); + subpanel.add(countField); lengthLabel.setFont(countAreaFont); countField.setFont(countAreaFont); @@ -1949,7 +1953,7 @@ public Dimension getMaximumSize() { }; spane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS); combinedPanel.add(spane); - combinedPanel.add(lengthPanel); + combinedPanel.add(subpanel); p3.add(combinedPanel); @@ -2053,8 +2057,12 @@ public void actionPerformed(java.awt.event.ActionEvent e) { entry.reload(); } }); - p3.add(b); - + if (subpanel != null ) { + subpanel.add(b); + } else { + p3.add(b); + } + writeButton = factory.handleWriteButton(new JButton("Write")); writeButton.addActionListener(new java.awt.event.ActionListener() { @Override @@ -2062,7 +2070,11 @@ public void actionPerformed(java.awt.event.ActionEvent e) { writeDisplayTextToNode(); } }); - p3.add(writeButton); + if (subpanel != null ) { + subpanel.add(writeButton); + } else { + p3.add(writeButton); + } } additionalButtons(); From d480af1a10a7d5768d7e8e2d4b21b2bd73787f80 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Fri, 22 Nov 2024 09:04:22 -0500 Subject: [PATCH 05/15] add readOnly attribute on Group; add link element 3 places --- sample.xml | 40 +++------ src/org/openlcb/cdi/CdiRep.java | 7 ++ .../cdi/impl/ConfigRepresentation.java | 10 +++ src/org/openlcb/cdi/jdom/JdomCdiRep.java | 61 ++++++++++++++ src/org/openlcb/cdi/swing/CdiPanel.java | 81 ++++++++++++++++--- 5 files changed, 157 insertions(+), 42 deletions(-) diff --git a/sample.xml b/sample.xml index 6c03b46f..046249ac 100644 --- a/sample.xml +++ b/sample.xml @@ -8,17 +8,21 @@ Model 123 Uniblab EC 415 1.2.3.4 + Link to OpenLCB.org documentation Size8 cm by 12 cm + Link to OpenLCB.org documentation Produced Events The EventIDs for the producers + Link to OpenLCB.org documentation - @@ -27,7 +31,7 @@ Consumed Events The EventIDs for the consumers - @@ -40,18 +44,18 @@ Int of size 4 so that each group is 32 long - + Hideable and Hidden Nested Group - - + Hideable and Not Hidden Nested Group - @@ -105,30 +109,6 @@ - - Sample Single Group - \n\ - - - \n\ - Output On Period\n\ - \n\ - \n\ - Output Off Period\n\ - \n\ - \n\ - Units\n\ - "xstr(UNITS_DEFAULT)"\n\ - \n\ - "xstr(UNITS_MS)"Milliseconds (ms)\n\ - "xstr(UNITS_S)"Seconds (s)\n\ - "xstr(UNITS_M)"Minutes (m)\n\ - \n\ - \n\ - \n\ - - Reset via Map diff --git a/src/org/openlcb/cdi/CdiRep.java b/src/org/openlcb/cdi/CdiRep.java index fbc5cde5..c8bf80b9 100644 --- a/src/org/openlcb/cdi/CdiRep.java +++ b/src/org/openlcb/cdi/CdiRep.java @@ -15,6 +15,8 @@ public static interface Identification { public String getModel(); public String getHardwareVersion(); public String getSoftwareVersion(); + public String getLinkText(); + public String getLinkURL(); public Map getMap(); } @@ -29,6 +31,8 @@ public static interface Segment { public String getName(); public String getDescription(); + public String getLinkText(); + public String getLinkURL(); public Map getMap(); public int getIndexInParent(); } @@ -44,9 +48,12 @@ public static interface Item { public static interface Group extends Item { public java.util.List getItems(); public int getReplication(); + public String getLinkText(); + public String getLinkURL(); public String getRepName(int index, int replications); public boolean isHideable(); public boolean isHidden(); + public boolean isReadOnly(); } public static interface Map { diff --git a/src/org/openlcb/cdi/impl/ConfigRepresentation.java b/src/org/openlcb/cdi/impl/ConfigRepresentation.java index 51a705d9..cc22cadd 100644 --- a/src/org/openlcb/cdi/impl/ConfigRepresentation.java +++ b/src/org/openlcb/cdi/impl/ConfigRepresentation.java @@ -412,6 +412,10 @@ public void reload() { MemorySpaceCache cache = getCacheForSpace(space); cache.reload(origin, size, isNullTerminated()); } + + boolean flaggedReadOnly = false; + public boolean isFlaggedReadOnly() { return flaggedReadOnly; } + public void setFlaggedReadOnly(boolean state) {flaggedReadOnly = state; } } public class Root implements CdiContainer { @@ -583,6 +587,12 @@ public boolean isHidden() { return group.isHidden(); } + /** + * Does this entry carry the readOnly hint? + */ + public boolean isReadOnlyConfigured() { + return group.isReadOnly(); + } } /** diff --git a/src/org/openlcb/cdi/jdom/JdomCdiRep.java b/src/org/openlcb/cdi/jdom/JdomCdiRep.java index 45b5c283..74853982 100644 --- a/src/org/openlcb/cdi/jdom/JdomCdiRep.java +++ b/src/org/openlcb/cdi/jdom/JdomCdiRep.java @@ -50,6 +50,22 @@ public String getSoftwareVersion() { return c.getText(); } + @Override + public String getLinkText() { + Element c = id.getChild("link"); + if (c == null) return null; + return c.getText(); + } + + @Override + public String getLinkURL() { + Element c = id.getChild("link"); + if (c == null) return null; + Attribute a = c.getAttribute("ref"); + if (a == null) return null; + return a.getValue(); + } + @Override public Map getMap() { return new Map(id.getChild("map")); @@ -128,6 +144,7 @@ public java.util.List getItems() { case "repname": case "name": case "description": + case "link": case "hints": break; default: @@ -166,6 +183,23 @@ public int getOrigin() { else return a.getIntValue(); } catch (org.jdom2.DataConversionException e1) { return 0; } } + + @Override + public String getLinkText() { + Element c = e.getChild("link"); + if (c == null) return null; + return c.getText(); + } + + @Override + public String getLinkURL() { + Element c = e.getChild("link"); + if (c == null) return null; + Attribute a = c.getAttribute("ref"); + if (a == null) return null; + return a.getValue(); + } + } public static class Map implements CdiRep.Map { @@ -291,6 +325,23 @@ public int getOffset() { } catch (org.jdom2.DataConversionException e1) { return 0; } } + @Override + public String getLinkText() { + Element c = e.getChild("link"); + if (c == null) return null; + return c.getText(); + } + + @Override + public String getLinkURL() { + Element c = e.getChild("link"); + if (c == null) return null; + Attribute a = c.getAttribute("ref"); + if (a == null) return null; + return a.getValue(); + } + + @Override public boolean isHideable() { // defaults to false Element hints = e.getChild("hints"); @@ -307,6 +358,7 @@ public boolean isHideable() { } } + @Override public boolean isHidden() { // defaults to false Element hints = e.getChild("hints"); @@ -323,6 +375,15 @@ public boolean isHidden() { } } + @Override + public boolean isReadOnly() { + // defaults to false + Element hints = e.getChild("hints"); + if (hints == null) return false; + Element readOnly = hints.getChild("readOnly"); + return readOnly != null; + } + /** * Provides the name for this replication. See the CDI TN for the * algorithm being used. diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 4ce0faa8..06b4b944 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -1098,6 +1098,17 @@ public void visitGroup(ConfigRepresentation.GroupEntry e) { } factory.handleGroupPaneStart(groupPane); + if (e.isReadOnlyConfigured()) { + // mark the direct children of these, except groups, as readOnly + for (ConfigRepresentation.CdiEntry entry : e.getEntries()) { + if (! (entry instanceof ConfigRepresentation.GroupEntry) ) { + System.out.println("Non Group Entry"); + entry.setFlaggedReadOnly(true); + } else { + System.out.println("Group Entry"); + } + } + } super.visitGroup(e); factory.handleGroupPaneEnd(groupPane); @@ -1559,7 +1570,7 @@ JPanel createIdentificationPane(CdiRep c) { JPanel p1 = new JPanel(); p.add(p1); - p1.setLayout(new util.javaworld.GridLayout2(4,2)); + p1.setLayout(new util.javaworld.GridLayout2(5,2)); p1.setAlignmentX(Component.LEFT_ALIGNMENT); p1.add(new JLabel("Manufacturer: ")); @@ -1574,6 +1585,10 @@ JPanel createIdentificationPane(CdiRep c) { p1.add(new JLabel("Software Version: ")); p1.add(new JLabel(id.getSoftwareVersion())); + if (id.getLinkText() != null && id.getLinkURL() != null) { + p1.add(new HtmlLabel(id.getLinkText(),id.getLinkURL())); + } + p1.setMaximumSize(p1.getPreferredSize()); // include map if present @@ -1629,7 +1644,8 @@ public class SegmentPane extends JPanel { //p.setBorder(BorderFactory.createTitledBorder(name)); createDescriptionPane(this, item.getDescription()); - + createLinkPane(this, item.segment.getLinkText(), item.segment.getLinkURL()); + // include map if present JPanel p2 = createPropertyPane(item.getMap()); if (p2 != null) p.add(p2); @@ -1655,6 +1671,12 @@ public Dimension getMaximumSize() { parent.add(area); } + void createLinkPane(JPanel parent, String text, String ref) { + if (text == null || ref == null) return; + parent.add(new HtmlLabel(text, ref)); + } + + private void addCopyPasteButtons(JPanel linePanel, JTextField textField) { final JButton b = new JButton("Copy"); final Color defaultColor = b.getBackground(); @@ -1858,6 +1880,7 @@ public class GroupPane extends JPanel { setName(name); createDescriptionPane(this, item.getDescription()); + createLinkPane(this, entry.group.getLinkText(), entry.group.getLinkURL()); // include map if present JPanel p2 = createPropertyPane(item.getMap()); @@ -2063,17 +2086,20 @@ public void actionPerformed(java.awt.event.ActionEvent e) { p3.add(b); } - writeButton = factory.handleWriteButton(new JButton("Write")); - writeButton.addActionListener(new java.awt.event.ActionListener() { - @Override - public void actionPerformed(java.awt.event.ActionEvent e) { - writeDisplayTextToNode(); + // write button is suppressed if flagged as read only + if (entry.isFlaggedReadOnly()) { + writeButton = factory.handleWriteButton(new JButton("Write")); + writeButton.addActionListener(new java.awt.event.ActionListener() { + @Override + public void actionPerformed(java.awt.event.ActionEvent e) { + writeDisplayTextToNode(); + } + }); + if (subpanel != null ) { + subpanel.add(writeButton); + } else { + p3.add(writeButton); } - }); - if (subpanel != null ) { - subpanel.add(writeButton); - } else { - p3.add(writeButton); } } @@ -2967,4 +2993,35 @@ public JTextArea handleEditorValue(JTextArea value) { return value; } } + + /** + * Implements the "link" element by providing a line of + * text that serves as an active hyperlink. + * Neither argument can be null. + */ + class HtmlLabel extends javax.swing.JTextPane { + public HtmlLabel(String text, String ref) { + super(); + setContentType("text/html"); + String content = ""+text+""; + setText(content); + + setAlignmentX(Component.LEFT_ALIGNMENT); + setFont(UIManager.getFont("TextArea.font")); + setEditable(false); + setOpaque(false); + + this.addHyperlinkListener(new javax.swing.event.HyperlinkListener() { + public void hyperlinkUpdate(javax.swing.event.HyperlinkEvent e) { + try { + if (e.getEventType() == javax.swing.event.HyperlinkEvent.EventType.ACTIVATED) { + if(java.awt.Desktop.isDesktopSupported()) { + java.awt.Desktop.getDesktop().browse(e.getURL().toURI()); + } + } + } catch (Exception ex) {} + } + }); + } + } } From d0a189150dd74bcbe6504b763aae1b37420df625 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Fri, 22 Nov 2024 09:27:04 -0500 Subject: [PATCH 06/15] handle readonly of groupreps --- src/org/openlcb/cdi/swing/CdiPanel.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 06b4b944..392a86aa 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -1100,12 +1100,15 @@ public void visitGroup(ConfigRepresentation.GroupEntry e) { factory.handleGroupPaneStart(groupPane); if (e.isReadOnlyConfigured()) { // mark the direct children of these, except groups, as readOnly + // when the direct child is a grouprep (repetitions > 1), mark those children for (ConfigRepresentation.CdiEntry entry : e.getEntries()) { if (! (entry instanceof ConfigRepresentation.GroupEntry) ) { - System.out.println("Non Group Entry"); - entry.setFlaggedReadOnly(true); - } else { - System.out.println("Group Entry"); + entry.setFlaggedReadOnly(true); + if (entry instanceof ConfigRepresentation.GroupRep ) { + for (ConfigRepresentation.CdiEntry subentry : ((ConfigRepresentation.GroupRep)entry).getEntries()) { + subentry.setFlaggedReadOnly(true); + } + } } } } @@ -2087,7 +2090,8 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } // write button is suppressed if flagged as read only - if (entry.isFlaggedReadOnly()) { + System.out.println("process "+entry.key); + if (! entry.isFlaggedReadOnly()) { writeButton = factory.handleWriteButton(new JButton("Write")); writeButton.addActionListener(new java.awt.event.ActionListener() { @Override From 981b7805fedb56273c5fbe6dd50e9a866f9798da Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Sun, 24 Nov 2024 05:09:13 -0500 Subject: [PATCH 07/15] rm debug --- src/org/openlcb/cdi/swing/CdiPanel.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 392a86aa..051a5947 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -2090,7 +2090,6 @@ public void actionPerformed(java.awt.event.ActionEvent e) { } // write button is suppressed if flagged as read only - System.out.println("process "+entry.key); if (! entry.isFlaggedReadOnly()) { writeButton = factory.handleWriteButton(new JButton("Write")); writeButton.addActionListener(new java.awt.event.ActionListener() { From ebb55e0366065df63404288a81aa323294bcd4ce Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Sun, 24 Nov 2024 05:09:54 -0500 Subject: [PATCH 08/15] show EWP payload --- .../ProducerConsumerEventReportMessage.java | 16 +++++++++++++--- .../ProducerConsumerEventReportMessageTest.java | 12 ++++++++++++ 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/src/org/openlcb/ProducerConsumerEventReportMessage.java b/src/org/openlcb/ProducerConsumerEventReportMessage.java index e9ac7278..5c0f7739 100644 --- a/src/org/openlcb/ProducerConsumerEventReportMessage.java +++ b/src/org/openlcb/ProducerConsumerEventReportMessage.java @@ -88,9 +88,19 @@ public byte[] getPayloadArray() { @Override public String toString() { - return super.toString() - +" Producer/Consumer Event Report "+eventID.toString() - +" payload of "+getPayloadSize(); + String retval = " Producer/Consumer Event Report "+eventID.toString(); + + if ( getPayloadSize() > 0 ) { + retval = retval + " payload of "+getPayloadSize()+" : "; + int n = getPayloadSize(); + boolean first = true; + for (byte data : payload) { + if (!first) retval = retval + "."; + retval = retval + Integer.toHexString((int)(data&0xFF)).toUpperCase(); + first = false; + } + } + return retval; } public boolean equals(Object o) { diff --git a/test/org/openlcb/ProducerConsumerEventReportMessageTest.java b/test/org/openlcb/ProducerConsumerEventReportMessageTest.java index 2d50e0c1..0f138dcb 100644 --- a/test/org/openlcb/ProducerConsumerEventReportMessageTest.java +++ b/test/org/openlcb/ProducerConsumerEventReportMessageTest.java @@ -84,6 +84,18 @@ public void testPayloadArray() { Assert.assertEquals(12, m2.getPayloadArray()[0]); } + @Test + public void testPayloadToString() { + ProducerConsumerEventReportMessage m1 = new ProducerConsumerEventReportMessage( + nodeID1, eventID1 ); + + byte[] payload1 = new byte[]{0x12, 0x34}; + ProducerConsumerEventReportMessage m2 = new ProducerConsumerEventReportMessage( + nodeID1, eventID1, payload1 ); + + Assert.assertEquals(" Producer/Consumer Event Report EventID:01.00.00.00.00.00.01.00 payload of 2 : 12.34", m2.toString()); + } + @Test public void testPayloadHashAndEquals() { ProducerConsumerEventReportMessage mNone = new ProducerConsumerEventReportMessage( From 469ddbfe00bd87d5aa2d5294cefcb4b05a7a2095 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Sun, 24 Nov 2024 07:56:30 -0500 Subject: [PATCH 09/15] showValue hint for int --- sample.xml | 24 ++- src/org/openlcb/cdi/CdiRep.java | 4 +- src/org/openlcb/cdi/jdom/JdomCdiRep.java | 23 ++- src/org/openlcb/cdi/swing/CdiPanel.java | 177 +++++++++++++++++++---- 4 files changed, 191 insertions(+), 37 deletions(-) diff --git a/sample.xml b/sample.xml index 046249ac..589261c3 100644 --- a/sample.xml +++ b/sample.xml @@ -94,7 +94,7 @@ 250 12 - + @@ -104,7 +104,27 @@ 1000 12 - + + + + + Sample integer slider with view + And another + 1 + 250 + 12 + + + + + + Immediate-write integer slider with view + You guessed it! + 0 + 1000 + 12 + + diff --git a/src/org/openlcb/cdi/CdiRep.java b/src/org/openlcb/cdi/CdiRep.java index c8bf80b9..d5a30085 100644 --- a/src/org/openlcb/cdi/CdiRep.java +++ b/src/org/openlcb/cdi/CdiRep.java @@ -111,8 +111,10 @@ public static interface IntegerRep extends Item { // Should the slider itself immediately write its value on change? public boolean isSliderImmediate(); // Optionally specifies the 'distance' between tick marks on the slider. - // If 0 (default value), don't show tick marks. + // If 0 (default value) or 1, don't show tick marks. public int getSliderTickSpacing(); + // Optionally specifies if the slider value should be shown in text box + public boolean isSliderShowValue(); } public static interface FloatRep extends Item { diff --git a/src/org/openlcb/cdi/jdom/JdomCdiRep.java b/src/org/openlcb/cdi/jdom/JdomCdiRep.java index 74853982..82a0a21b 100644 --- a/src/org/openlcb/cdi/jdom/JdomCdiRep.java +++ b/src/org/openlcb/cdi/jdom/JdomCdiRep.java @@ -550,8 +550,12 @@ public boolean isSliderImmediate() { if (slider == null) return false; Attribute immediate = slider.getAttribute("immediate"); if (immediate == null) return false; - if (! immediate.getValue().toLowerCase().equals("yes")) return false; - return true; + try { + boolean value = immediate.getBooleanValue(); + return value; + } catch (DataConversionException ex) { + return false; + } } @Override @@ -567,6 +571,21 @@ public int getSliderTickSpacing() { } catch (org.jdom2.DataConversionException e) { return 0; } } + @Override + public boolean isSliderShowValue() { + Element hints = e.getChild("hints"); + if (hints == null) return false; + Element slider = hints.getChild("slider"); + if (slider == null) return false; + Attribute showValue = slider.getAttribute("showValue"); + if (showValue == null) return false; + try { + boolean value = showValue.getBooleanValue(); + return value; + } catch (DataConversionException ex) { + return false; + } + } } diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 051a5947..dee9d2ee 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -32,7 +32,10 @@ import java.awt.event.ActionListener; import java.awt.event.ComponentAdapter; import java.awt.event.ComponentEvent; +import java.awt.event.FocusEvent; +import java.awt.event.FocusListener; import java.awt.event.InputEvent; +import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.awt.event.MouseAdapter; @@ -88,6 +91,8 @@ import javax.swing.SwingUtilities; import javax.swing.UIManager; import javax.swing.WindowConstants; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; import javax.swing.event.DocumentEvent; import javax.swing.event.DocumentListener; import javax.swing.text.AttributeSet; @@ -2012,6 +2017,8 @@ private void updateLength() { p3.add(textComponent); } textComponent.setMaximumSize(textComponent.getPreferredSize()); + + // Add color-setting listeners - this is here to avoid having lots of replicated code if (textComponent instanceof JTextComponent) { ((JTextComponent) textComponent).getDocument().addDocumentListener( new DocumentListener() { @@ -2035,20 +2042,8 @@ private void drawRed() { } } ); - } else if (textComponent instanceof JComboBox) { - ((JComboBox) textComponent).addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - updateColor(); - } - }); - } else if (textComponent instanceof JSlider) { - ((JSlider) textComponent).addChangeListener(new javax.swing.event.ChangeListener(){ - public void stateChanged(javax.swing.event.ChangeEvent e) { - updateColor(); - } - }); } + entryListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent propertyChangeEvent) { @@ -2448,11 +2443,112 @@ private void releaseListener() { } } + // represent a slider with an optional text view + private class SliderWithView extends JPanel { + JSlider slider = null; + JTextField textField = null; + + SliderWithView(int min, int max, boolean showValue, int size) { + setLayout(new FlowLayout()); + + // define the slider + slider = new JSlider(min, max); + slider.setOpaque(true); // so you can color it + + // set a tooltip showing the valid range + if (min < 0) { + slider.setToolTipText("Signed integer from " + +min+" to "+max + +" ("+size+" bytes)"); + } else { + slider.setToolTipText("Unsigned integer from " + +min+" to "+max + +" ("+size+" bytes)"); + } + + add(slider); + + // optionally define the text field + if (showValue) { + textField = new JTextField(2+(int)Math.log10(Math.max(1., Math.abs(max)))) { + public java.awt.Dimension getMaximumSize() { + return getPreferredSize(); + } + }; + textField.setOpaque(true); // so you can color it + + // set a tooltip showing the valid range + if (min < 0) { + textField.setToolTipText("Signed integer from " + +min+" to "+max + +" ("+size+" bytes)"); + } else { + textField.setToolTipText("Unsigned integer from " + +min+" to "+max + +" ("+size+" bytes)"); + } + + // add a listener to the slider to fill this + slider.addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + textField.setText(""+slider.getValue()); + } + }); + // Add listeners to set slider. Value is considered + // final when the field is exited or Enter is hit. + // We do this instead of listening for a value + // change to avoid a possible back-and-forth + // setting loop between the text field and slider. + textField.addFocusListener(new FocusListener(){ + public void focusLost(FocusEvent e) { + textToSlider(); + } + public void focusGained(FocusEvent e) { + } + }); + textField.addKeyListener(new KeyAdapter() { + @Override + public void keyReleased(KeyEvent ke) { + if (ke.getKeyCode() == KeyEvent.VK_ENTER) { + textToSlider(); + } + } + }); + + add(textField); + } + } + + // setting background also colors slider + @Override + public void setBackground(Color color) { + // super.setBackground(..) would color whole block + if (slider != null) { + slider.setBackground(color); + } + if (textField != null) { + textField.setBackground(color); + } + } + + // copies the textfield value to the slider, handling errors + void textToSlider() { + try { + int current = (int)Double.parseDouble(textField.getText().trim()); + slider.setValue(current); + } catch (NumberFormatException e) { + // don't set the value, load from current slider value + textField.setText(""+slider.getValue()); + } + } + } + private class IntPane extends EntryPane { JTextField textField = null; JComboBox box = null; - JSlider slider = null; + SliderWithView sliderView = null; CdiRep.Map map = null; private final ConfigRepresentation.IntegerEntry entry; boolean suppressExternal = false; // used to suppress slider output when changed from read @@ -2473,26 +2569,35 @@ public java.awt.Dimension getMaximumSize() { return getPreferredSize(); } }; + + // add color listener + box.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + updateColor(); + } + }); + textComponent = box; } else { // map not present - is it a slider? if (entry.rep.isSliderHint()) { // display a slider - slider = new JSlider((int)entry.rep.getMin(), (int)entry.rep.getMax()); - slider.setOpaque(true); // so you can color it - if (entry.rep.getSliderTickSpacing() > 1) { + sliderView = new SliderWithView((int)entry.rep.getMin(), (int)entry.rep.getMax(), entry.rep.isSliderShowValue(), entry.size); + + if (entry.rep.getSliderTickSpacing() > 0) { // default is zero // display divisions on the slider - slider.setMajorTickSpacing(entry.rep.getSliderTickSpacing()); - slider.setLabelTable(slider.createStandardLabels(entry.rep.getSliderTickSpacing())); - slider.setPaintTicks(true); - slider.setPaintLabels(true); + sliderView.slider.setMajorTickSpacing(entry.rep.getSliderTickSpacing()); + sliderView.slider.setLabelTable(sliderView.slider.createStandardLabels(entry.rep.getSliderTickSpacing())); + sliderView.slider.setPaintTicks(true); + sliderView.slider.setPaintLabels(true); } // (optionally) listen for changes and immediately write if (entry.rep.isSliderImmediate()) { - slider.addChangeListener(new javax.swing.event.ChangeListener(){ + sliderView.slider.addChangeListener(new javax.swing.event.ChangeListener(){ public void stateChanged(javax.swing.event.ChangeEvent e) { - if (!slider.getValueIsAdjusting()) { + if (!sliderView.slider.getValueIsAdjusting()) { if (!suppressInternal && !suppressExternal) { writeDisplayTextToNode(); } @@ -2503,15 +2608,23 @@ public void stateChanged(javax.swing.event.ChangeEvent e) { }); } - textComponent = slider; + + // add the listener that handles changed color + sliderView.slider.addChangeListener(new javax.swing.event.ChangeListener(){ + public void stateChanged(javax.swing.event.ChangeEvent e) { + updateColor(); + } + }); + + textComponent = sliderView; // set the tooltip to min and max values if (entry.rep.getMin() < 0) { - slider.setToolTipText("Signed integer from " + sliderView.setToolTipText("Signed integer from " +entry.rep.getMin()+" to "+entry.rep.getMax() +" ("+entry.size+" bytes)"); } else { - slider.setToolTipText("Unsigned integer from " + sliderView.setToolTipText("Unsigned integer from " +entry.rep.getMin()+" to "+entry.rep.getMax() +" ("+entry.size+" bytes)"); } @@ -2544,10 +2657,10 @@ protected void writeDisplayTextToNode() { long value; if (textField != null) { value = Long.parseLong(textField.getText()); - } else if (slider != null) { + } else if (sliderView != null) { // get value from current slider position suppressInternal = true; // will be set false once change works through - value = slider.getValue(); + value = sliderView.slider.getValue(); } else { // have to get key from stored map value String entry = (String) box.getSelectedItem(); @@ -2563,9 +2676,9 @@ protected void writeDisplayTextToNode() { @Override protected void updateDisplayText(@NonNull String value) { if (textField != null) textField.setText(value); - if (slider != null) { + if (sliderView != null) { suppressInternal = true; - slider.setValue(Integer.parseInt(value)); + sliderView.slider.setValue(Integer.parseInt(value)); } if (box != null) { // check to see if item exists @@ -2595,7 +2708,7 @@ protected void updateDisplayText(@NonNull String value) { @NonNull @Override protected String getDisplayText() { - if (slider != null) return ""+slider.getValue(); + if (sliderView != null) return ""+sliderView.slider.getValue(); String s = (box == null) ? (String) textField.getText() : (String) box.getSelectedItem(); return s == null ? "" : s; @@ -2610,7 +2723,7 @@ protected String getDisplayText() { */ @NonNull protected String getCurrentValue() { - if (slider != null) return ""+slider.getValue(); + if (sliderView != null) return ""+sliderView.slider.getValue(); String s; if (box==null) { From dd891a78748c11f9bb8d4219aaf81d0584502727 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Sun, 24 Nov 2024 13:22:02 -0500 Subject: [PATCH 10/15] fix minimum size of Event ID input field --- src/org/openlcb/swing/EventIdTextField.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/openlcb/swing/EventIdTextField.java b/src/org/openlcb/swing/EventIdTextField.java index 52a61345..8c360e84 100644 --- a/src/org/openlcb/swing/EventIdTextField.java +++ b/src/org/openlcb/swing/EventIdTextField.java @@ -64,6 +64,7 @@ public static JFormattedTextField getEventIdTextField() { // Let's size the event ID fields for the longest event ID in pixels. retval.setValue("DD.DD.DD.DD.DD.DD.DD.DD"); retval.setPreferredSize(retval.getPreferredSize()); + retval.setMinimumSize(retval.getPreferredSize()); retval.setValue("00.00.00.00.00.00.00.00"); retval.setToolTipText("EventID as eight-byte dotted-hex string, " From 0bfbd9fc32385b70e35bf79ae709f374b0eee837 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Sun, 24 Nov 2024 13:22:44 -0500 Subject: [PATCH 11/15] disable input when readonly --- src/org/openlcb/cdi/swing/CdiPanel.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index dee9d2ee..7f93b18c 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -115,7 +115,7 @@ * * Works with a CDI reader. * - * @author Bob Jacobsen Copyright 2011 + * @author Bob Jacobsen Copyright 2011, 2024 * @author Paul Bender Copyright 2016 * @author Balazs Racz Copyright 2016 * @author Pete Cressman Copyright 2020 @@ -2220,6 +2220,7 @@ public void propertyChange(PropertyChangeEvent propertyChangeEvent) { if (eventTable != null) { add(eventNamesLabel); } + if (e.isFlaggedReadOnly()) textComponent.setEnabled(false); } /** @@ -2533,6 +2534,17 @@ public void setBackground(Color color) { } } + @Override + public void setEnabled(boolean state) { + super.setEnabled(state); + if (slider != null) { + slider.setEnabled(state); + } + if (textField != null) { + textField.setEnabled(state); + } + } + // copies the textfield value to the slider, handling errors void textToSlider() { try { @@ -2650,6 +2662,7 @@ public java.awt.Dimension getMaximumSize() { } init(); + if (e.isFlaggedReadOnly()) textComponent.setEnabled(false); } @Override @@ -2786,6 +2799,7 @@ public java.awt.Dimension getMaximumSize() { +" ("+entry.size+" bytes)"); init(); + if (e.isFlaggedReadOnly()) textComponent.setEnabled(false); } @Override @@ -2906,6 +2920,7 @@ public Dimension getMaximumSize() { textComponent = textField; textComponent.setToolTipText("String of up to "+(entry.size-1)+" characters"); // -1 for terminating zero in field init(); + if (e.isFlaggedReadOnly()) textComponent.setEnabled(false); } @Override From 089f91f524cf692f6fcc7b2496b30f4a8223debc Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Mon, 25 Nov 2024 08:46:45 -0500 Subject: [PATCH 12/15] add radio button --- src/org/openlcb/cdi/CdiRep.java | 2 + src/org/openlcb/cdi/jdom/JdomCdiRep.java | 10 ++ src/org/openlcb/cdi/swing/CdiPanel.java | 132 +++++++++++++++++++---- 3 files changed, 126 insertions(+), 18 deletions(-) diff --git a/src/org/openlcb/cdi/CdiRep.java b/src/org/openlcb/cdi/CdiRep.java index d5a30085..6864cb29 100644 --- a/src/org/openlcb/cdi/CdiRep.java +++ b/src/org/openlcb/cdi/CdiRep.java @@ -115,6 +115,8 @@ public static interface IntegerRep extends Item { public int getSliderTickSpacing(); // Optionally specifies if the slider value should be shown in text box public boolean isSliderShowValue(); + // Did the CDI content hint that this value should be presented as a radio button? + public boolean isRadioButtonHint(); } public static interface FloatRep extends Item { diff --git a/src/org/openlcb/cdi/jdom/JdomCdiRep.java b/src/org/openlcb/cdi/jdom/JdomCdiRep.java index 82a0a21b..86f8569f 100644 --- a/src/org/openlcb/cdi/jdom/JdomCdiRep.java +++ b/src/org/openlcb/cdi/jdom/JdomCdiRep.java @@ -586,6 +586,16 @@ public boolean isSliderShowValue() { return false; } } + + @Override + public boolean isRadioButtonHint() { + Element hints = e.getChild("hints"); + if (hints == null) return false; + Element radiobutton = hints.getChild("radiobutton"); + if (radiobutton == null) return false; + return true; + } + } diff --git a/src/org/openlcb/cdi/swing/CdiPanel.java b/src/org/openlcb/cdi/swing/CdiPanel.java index 7f93b18c..47a38d6f 100644 --- a/src/org/openlcb/cdi/swing/CdiPanel.java +++ b/src/org/openlcb/cdi/swing/CdiPanel.java @@ -52,6 +52,7 @@ import java.io.StringReader; import java.util.ArrayList; import java.util.Collections; +import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -64,10 +65,12 @@ import java.util.regex.Pattern; import javax.swing.AbstractAction; +import javax.swing.AbstractButton; import javax.swing.Action; import javax.swing.BorderFactory; import javax.swing.Box; import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; import javax.swing.ComboBoxModel; import javax.swing.InputVerifier; import javax.swing.JButton; @@ -87,6 +90,7 @@ import javax.swing.JTextArea; import javax.swing.JTextField; import javax.swing.KeyStroke; +import javax.swing.JRadioButton; import javax.swing.ScrollPaneConstants; import javax.swing.SwingUtilities; import javax.swing.UIManager; @@ -2557,10 +2561,73 @@ void textToSlider() { } } + // represents a set of radio buttons + private class RadioButtonPane extends JPanel { + + CdiRep.Map map; + ButtonGroup group = new ButtonGroup(); + + RadioButtonPane(CdiRep.Map map, ActionListener action) { + this.map = map; + + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + // create the pane and fill with radio buttons + for (String v : map.getValues()) { + JRadioButton button = new JRadioButton(v); + button.setActionCommand(v); + add(button); + // add color listener + button.addActionListener(action); + + group.add(button); + } + } + + long getCurrentValue() { + return Long.parseLong(getCurrentValueString()); + } + + // value is a numeric string + void setCurrentValue(String value) { + String key = map.getKey(value); + + Enumeration e = group.getElements(); + while (e.hasMoreElements()) { + AbstractButton b = e.nextElement(); + if (b.getActionCommand().equals(value)) { + b.setSelected(true); + return; + } + } + // else set unselected + logger.log(Level.WARNING, "Value \""+value+"\" does not match a button value, taking 1st button"); + group.clearSelection(); + } + + String getCurrentValueString() { + String value = getDisplayText(); + if (map.getKey(value) != null) { + return map.getKey(value); + } else { + logger.severe("Value \""+value+"\" does not match a button name"); + return map.getKeys().get(0); + } + } + + String getDisplayText() { + if (group.getSelection() == null) { + return map.getValues().get(0); + } + return group.getSelection().getActionCommand(); + } + + } + private class IntPane extends EntryPane { JTextField textField = null; JComboBox box = null; SliderWithView sliderView = null; + RadioButtonPane radiobuttons = null; CdiRep.Map map = null; private final ConfigRepresentation.IntegerEntry entry; boolean suppressExternal = false; // used to suppress slider output when changed from read @@ -2575,22 +2642,33 @@ private class IntPane extends EntryPane { String[] labels; map = item.getMap(); if ((map != null) && (map.getKeys().size() > 0)) { - // map present, make selection box - box = new JComboBox(map.getValues().toArray(new String[]{""})) { - public java.awt.Dimension getMaximumSize() { - return getPreferredSize(); - } - }; - - // add color listener - box.addActionListener(new ActionListener() { - @Override - public void actionPerformed(ActionEvent actionEvent) { - updateColor(); - } - }); - - textComponent = box; + // map present, make selection box or radio buttons? + if (entry.rep.isRadioButtonHint()) { + ActionListener action = new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + updateColor(); + } + }; + radiobuttons = new RadioButtonPane(map, action); + textComponent = radiobuttons; + } else { + box = new JComboBox(map.getValues().toArray(new String[]{""})) { + public java.awt.Dimension getMaximumSize() { + return getPreferredSize(); + } + }; + + // add color listener + box.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent actionEvent) { + updateColor(); + } + }); + + textComponent = box; + } } else { // map not present - is it a slider? if (entry.rep.isSliderHint()) { @@ -2665,6 +2743,7 @@ public java.awt.Dimension getMaximumSize() { if (e.isFlaggedReadOnly()) textComponent.setEnabled(false); } + // Takes the current GUI content and writes it to the node @Override protected void writeDisplayTextToNode() { long value; @@ -2674,6 +2753,8 @@ protected void writeDisplayTextToNode() { // get value from current slider position suppressInternal = true; // will be set false once change works through value = sliderView.slider.getValue(); + } else if (radiobuttons != null) { + value = radiobuttons.getCurrentValue(); } else { // have to get key from stored map value String entry = (String) box.getSelectedItem(); @@ -2687,11 +2768,14 @@ protected void writeDisplayTextToNode() { } @Override + // Sets a specific value into the GUI protected void updateDisplayText(@NonNull String value) { if (textField != null) textField.setText(value); if (sliderView != null) { suppressInternal = true; sliderView.slider.setValue(Integer.parseInt(value)); + } else if (radiobuttons != null) { + radiobuttons.setCurrentValue(value); } if (box != null) { // check to see if item exists @@ -2720,8 +2804,16 @@ protected void updateDisplayText(@NonNull String value) { @NonNull @Override + /* + * Returns the current content of the GUI as a String + * This may be a number, but for a map (or buttons) it's the current label + */ protected String getDisplayText() { - if (sliderView != null) return ""+sliderView.slider.getValue(); + if (sliderView != null) { + return ""+sliderView.slider.getValue(); + } else if (radiobuttons != null) { + return radiobuttons.getDisplayText(); + } String s = (box == null) ? (String) textField.getText() : (String) box.getSelectedItem(); return s == null ? "" : s; @@ -2736,7 +2828,11 @@ protected String getDisplayText() { */ @NonNull protected String getCurrentValue() { - if (sliderView != null) return ""+sliderView.slider.getValue(); + if (sliderView != null) { + return ""+sliderView.slider.getValue(); + } else if (radiobuttons != null) { + return radiobuttons.getCurrentValueString(); + } String s; if (box==null) { From 064102a5a8d19d1f3426c71f584ff3f0a3b0af3d Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Mon, 25 Nov 2024 08:47:36 -0500 Subject: [PATCH 13/15] add radio button --- sample.xml | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/sample.xml b/sample.xml index 589261c3..1e49fc12 100644 --- a/sample.xml +++ b/sample.xml @@ -16,7 +16,7 @@ Link to OpenLCB.org documentation - + Produced Events The EventIDs for the producers Link to OpenLCB.org documentation @@ -26,8 +26,23 @@ + + + 1 + 250 + 12 + + + + + + + Status Field + As first string, this content will appear in the tab + + - + Consumed Events The EventIDs for the consumers @@ -137,10 +152,13 @@ Board must be restarted for this to take effect. - 0No reset (0) + 10No reset (10) 85Reset just EventIDs to defaults (85) 170Reset all to defaults (170) + + + Reset Directly From d0a8c2eb7d525e7ac974c18a492cb73e2c320235 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Wed, 27 Nov 2024 08:00:45 -0500 Subject: [PATCH 14/15] API change - make method public --- src/org/openlcb/ProtocolIdentification.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/openlcb/ProtocolIdentification.java b/src/org/openlcb/ProtocolIdentification.java index ee19f26a..2cdda57e 100644 --- a/src/org/openlcb/ProtocolIdentification.java +++ b/src/org/openlcb/ProtocolIdentification.java @@ -105,7 +105,7 @@ public List getProtocolNames() { * @param protocol enum representing the protocol bit to test * @return true if protocol is supported, false otherwise. */ - boolean hasProtocol(Protocol protocol) { + public boolean hasProtocol(Protocol protocol) { return protocol.supports(value); } } From 2e22c6e481d85cc9aae46ba32a24f365eb857619 Mon Sep 17 00:00:00 2001 From: Bob Jacobsen Date: Wed, 27 Nov 2024 10:34:35 -0500 Subject: [PATCH 15/15] add set selector --- src/org/openlcb/swing/NodeSelector.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/org/openlcb/swing/NodeSelector.java b/src/org/openlcb/swing/NodeSelector.java index dd9a7dab..7fbe9f26 100644 --- a/src/org/openlcb/swing/NodeSelector.java +++ b/src/org/openlcb/swing/NodeSelector.java @@ -272,5 +272,14 @@ public NodeID getSelectedNodeID() { return me.getNodeID(); } + public void setSelectedNodeID(NodeID nodeID) { + for (int i = 0; i < model.getSize(); ++i) { + if (model.getElementAt(i).getNodeID().equals(nodeID)) { + super.setSelectedItem(model.getElementAt(i)); + break; + } + } + } + private static final Logger log = Logger.getLogger(NodeSelector.class.getName()); }