001package jmri.jmrit.throttle.panels; 002 003import java.awt.BorderLayout; 004import java.awt.Component; 005import java.util.ArrayList; 006import java.util.List; 007 008import javax.swing.Icon; 009import javax.swing.JLabel; 010import javax.swing.JPanel; 011import javax.swing.JTabbedPane; 012 013import org.jdom2.Element; 014 015import jmri.Consist; 016import jmri.DccLocoAddress; 017import jmri.DccThrottle; 018import jmri.InstanceManager; 019import jmri.LocoAddress; 020import jmri.ThrottleManager; 021import jmri.jmrit.roster.Roster; 022import jmri.jmrit.roster.RosterEntry; 023import jmri.jmrit.roster.RosterIconFactory; 024import jmri.jmrit.throttle.implementation.SimpleThrottlePanel; 025import jmri.jmrit.throttle.interfaces.AddressListener; 026 027/** 028 * A panel do be used withing a throttle UI, allows for independantly 029 * controling functions of each locomotive in a consist 030 * 031 * <hr> 032 * This file is part of JMRI. 033 * <p> 034 * JMRI is free software; you can redistribute it and/or modify it under the 035 * terms of version 2 of the GNU General Public License as published by the Free 036 * Software Foundation. See the "COPYING" file for a copy of this license. 037 * <p> 038 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 039 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 040 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 041 * 042 * @author Lionel Jeanson Copyright (C) 2026 043 * 044 */ 045 046public class ConsistFunctionPanel extends JPanel implements AddressListener { 047 048 private final ThrottleManager throttleManager; 049 private AddressPanel addressPanel = null; 050 private JTabbedPane consistFunctionsPanels; 051 private JLabel errorLabel = new JLabel(Bundle.getMessage("ThrottleConsistsFunctionPanelError")); 052 private static final RosterIconFactory ICN_FACT = InstanceManager.getDefault(RosterIconFactory.class); 053 054 public ConsistFunctionPanel(ThrottleManager tm) { 055 throttleManager = tm; 056 initGUI(); 057 updateFunctionPanels(); 058 } 059 060 private void initGUI() { 061 setLayout(new BorderLayout()); 062 consistFunctionsPanels = new JTabbedPane(); 063 add(errorLabel, BorderLayout.NORTH); 064 add(consistFunctionsPanels, BorderLayout.CENTER); 065 } 066 067 public void dispose() { 068 if (consistFunctionsPanels!=null) { 069 for (Component cmp : consistFunctionsPanels.getComponents()) { 070 if (cmp instanceof SimpleThrottlePanel) { 071 ((SimpleThrottlePanel) cmp).dispose(); 072 } 073 } 074 consistFunctionsPanels.removeAll(); 075 } 076 consistFunctionsPanels = null; 077 } 078 079 public void setAddressPanel(AddressPanel addressPanel) { 080 if (this.addressPanel != null) { 081 this.addressPanel.removeAddressListener(this); 082 } 083 this.addressPanel = addressPanel; 084 if (this.addressPanel != null) { 085 this.addressPanel.addAddressListener(this); 086 } 087 updateFunctionPanels(); 088 } 089 090 public DccThrottle getFunctionThrottle() { 091 if (consistFunctionsPanels == null || consistFunctionsPanels.getComponentCount() == 0) { 092 return null; 093 } 094 Component lastCmp = consistFunctionsPanels.getComponentAt(consistFunctionsPanels.getComponentCount()-1); 095 if (! (lastCmp instanceof SimpleThrottlePanel)) { 096 return null; 097 } 098 // the throttle of the last panel will be the head unit one, we'll use that one for consist functions 099 return ((SimpleThrottlePanel)lastCmp).getThrottle(); 100 } 101 102 public RosterEntry getFunctionRosterEntry() { 103 if (consistFunctionsPanels == null || consistFunctionsPanels.getComponentCount() == 0) { 104 return null; 105 } 106 Component lastCmp = consistFunctionsPanels.getComponentAt(consistFunctionsPanels.getComponentCount()-1); 107 if (! (lastCmp instanceof SimpleThrottlePanel)) { 108 return null; 109 } 110 // the throttle of the last panel will be the head unit one, we'll use that one for consist functions 111 return ((SimpleThrottlePanel)lastCmp).getRosterEntry(); 112 } 113 114 private void updateFunctionPanels() { 115 if (consistFunctionsPanels == null) { 116 errorLabel.setVisible(true); 117 return; // we're self destructing 118 } 119 // clean up 120 consistFunctionsPanels.removeAll(); 121 for (Component cmp : consistFunctionsPanels.getComponents()) { 122 if (cmp instanceof SimpleThrottlePanel) { 123 ((SimpleThrottlePanel) cmp).dispose(); 124 } 125 } 126 // shall we? 127 if ((addressPanel == null) || (addressPanel.getThrottle() == null)) { 128 return; 129 } 130 Consist consist = addressPanel.getConsistEntry(); 131 if (consist == null) { 132 return; 133 } 134 // let's go 135 errorLabel.setVisible(false); 136 ArrayList<DccLocoAddress> consistList = consist.getConsistList(); 137 // go backward, we want the head on the right (added last) 138 for (int i=consistList.size()-1; i>=0; i--) { 139 SimpleThrottlePanel stp = new SimpleThrottlePanel(null, throttleManager, false, true, false); 140 stp.setAddress(consistList.get(i)); 141 Icon tabIcon = null; 142 String tabText = Bundle.getMessage("ThrottleAddress") + " " + consistList.get(i); 143 // do we have a matching roster entry 144 List<RosterEntry> l = Roster.getDefault().matchingList(null, null, "" + consistList.get(i).getNumber(), null, null, null, null); 145 if (!l.isEmpty()) { 146 tabText = l.get(0).getId(); 147 if (consist.getLocoDirection(consistList.get(i))) { 148 tabIcon = ICN_FACT.getIcon(l.get(0)); 149 } else { 150 tabIcon = ICN_FACT.getReversedIcon(l.get(0)); 151 } 152 } 153 consistFunctionsPanels.addTab(tabText, tabIcon, stp); 154 } 155 consistFunctionsPanels.setSelectedIndex(consistList.size()-1); // select last (head) 156 } 157 158 @Override 159 public void notifyConsistAddressReleased(LocoAddress address) { 160 updateFunctionPanels(); 161 } 162 163 @Override 164 public void notifyConsistAddressThrottleFound(DccThrottle throttle) { 165 updateFunctionPanels(); 166 } 167 168 @Override 169 public void notifyConsistAddressChosen(LocoAddress address) { 170 // do nothing 171 } 172 173 @Override 174 public void notifyAddressChosen(LocoAddress address) { 175 // do nothing 176 } 177 178 @Override 179 public void notifyAddressReleased(LocoAddress address) { 180 // do nothing 181 } 182 183 @Override 184 public void notifyAddressThrottleFound(DccThrottle throttle) { 185 // do nothing 186 } 187 188 @Override 189 public void notifyRosterEntrySelected(RosterEntry re) { 190 updateFunctionPanels(); 191 } 192 193 public Element getXml() { 194 Element me = new Element("ConsistFunctionsPanel"); // NOI18N 195 // put nothing 196 return me; 197 } 198 199 public void setXml(Element e) { 200 // do nothing 201 } 202 203}