001package jmri.jmrix.rfid.swing.tagcarwin; 002 003import jmri.IdTagManager; 004import jmri.InstanceManager; 005import jmri.UserPreferencesManager; 006import jmri.jmrit.operations.rollingstock.cars.Car; 007import jmri.jmrit.operations.rollingstock.cars.CarManager; 008import jmri.util.AlphanumComparator; 009import jmri.util.JmriJFrame; 010import jmri.util.swing.JmriPanel; 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import java.awt.*; 015import java.awt.event.ActionEvent; 016import java.awt.event.ActionListener; 017import java.util.ArrayList; 018import java.util.Hashtable; 019import java.util.List; 020import javax.swing.*; 021import javax.swing.event.ListSelectionEvent; 022import javax.swing.event.ListSelectionListener; 023 024/** 025 * A panel to associate an RFID tag with a car 026 * 027 * @author J. Scott Walton Copyright (C) 2022 028 */ 029public class AssociateTag extends JmriPanel implements ActionListener, ListSelectionListener { 030 private static final Logger log = LoggerFactory.getLogger(AssociateTag.class); 031 032 String tag; 033 private final JButton okayButton = new JButton(); 034 private final JButton cancelButton = new JButton(); 035 private final DefaultListModel<String> roadListModel = new DefaultListModel<>(); 036 private final DefaultListModel<String> numberListModel = new DefaultListModel<>(); 037 private final JList<String> roadCombo = new JList<>(); 038 private final JList<String> numberCombo = new JList<>(); 039 040 041 private final JCheckBox includeCars = new JCheckBox(); 042 private final JLabel message = new JLabel(""); 043 private final String includeCarsString = this.getClass().getName() + "IncludeAllCars"; 044 private final ArrayList<String> roadList = new ArrayList<>(); 045 private final ArrayList<String> roadsWith = new ArrayList<>(); 046 private final Hashtable<String, List<String>> numbersWith = new Hashtable<>(); 047 private final Hashtable<String, List<String>> roadNumbers = new Hashtable<>(); 048 protected CarManager carManager = InstanceManager.getDefault(CarManager.class); 049 protected IdTagManager tagManager = InstanceManager.getDefault(IdTagManager.class); 050 051 public void setParentFrame(JmriJFrame parentFrame) { 052 this.parentFrame = parentFrame; 053 } 054 055 JmriJFrame parentFrame; 056 057 public AssociateTag(String thisTag) { 058 super(); 059 tag = thisTag; 060 } 061 062 063 @Override 064 public String getTitle(){ 065 return Bundle.getMessage("AssociateTitle" ) + tag; 066 } 067 068 @Override 069 public void dispose() { 070 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 071 pm.setSimplePreferenceState(includeCarsString, includeCars.isSelected()); 072 } 073 074 @Override 075 public void initComponents() { 076 log.debug("setting up the AssociateTag panel"); 077 initRoads(); 078 this.setLayout(new GridBagLayout()); 079 this.setPreferredSize(new Dimension(500, 500)); 080 this.setMinimumSize(new Dimension(500, 450)); 081 082 083 UserPreferencesManager pm = InstanceManager.getDefault(UserPreferencesManager.class); 084 includeCars.setSelected(pm.getSimplePreferenceState(includeCarsString)); 085 JScrollPane roadScroll = new JScrollPane(); 086 roadScroll.setMinimumSize(new Dimension(150, 250)); 087 roadScroll.setViewportView(roadCombo); 088 roadCombo.setVisibleRowCount(10); 089 roadCombo.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 090 roadCombo.setLayoutOrientation(JList.VERTICAL); 091 roadCombo.addListSelectionListener(this); 092 roadCombo.setMinimumSize(new Dimension(150, 250)); 093 roadCombo.setModel(roadListModel); 094 this.add(roadScroll, new GridBagConstraints(0, 0, 1, 2, 0.0, 0.0, 095 GridBagConstraints.LINE_START, GridBagConstraints.BOTH, 096 new Insets(10, 10, 10, 10), 10, 10)); 097 098 JScrollPane numberScroll = new JScrollPane(); 099 numberScroll.setViewportView(numberCombo); 100 numberScroll.setMinimumSize(new Dimension(150, 250)); 101 numberCombo.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 102 numberCombo.setVisibleRowCount(10); 103 numberCombo.setLayoutOrientation(JList.VERTICAL); 104 numberCombo.setMinimumSize(new Dimension(75, 75)); 105 numberCombo.setEnabled(false); 106 numberCombo.setModel(numberListModel); 107 108 this.add(numberScroll, new GridBagConstraints( 2, 0, 1, 2, 0.0, 0.0, 109 GridBagConstraints.LINE_END, GridBagConstraints.BOTH, 110 new Insets(10, 10, 10, 10), 10, 10)); 111 JLabel tagLabel = new JLabel(); 112 tagLabel.setText(Bundle.getMessage("AssociateTag")); 113 this.add(tagLabel, new GridBagConstraints(0, 4, 2, 1, 0.0, 0.0, 114 GridBagConstraints.LINE_END, GridBagConstraints.HORIZONTAL, 115 new Insets(5, 5, 5, 5), 5, 5)); 116 JLabel thisTag = new JLabel(tag); 117 this.add(thisTag, new GridBagConstraints(2, 4, 1, 1, 0.0, 0.0, 118 GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, 119 new Insets(5, 5, 5, 5), 5, 5)); 120 includeCars.setText(Bundle.getMessage("AssociateIncludeAll")); 121 includeCars.setToolTipText(Bundle.getMessage("AssociateAllToolTip")); 122 includeCars.addActionListener(this); 123 this.add(includeCars, new GridBagConstraints(0, 5, 3, 1, 0.0, 0.0, 124 GridBagConstraints.LINE_START, GridBagConstraints.HORIZONTAL, 125 new Insets(5, 5, 5, 5), 0, 0)); 126 message.setMinimumSize(new Dimension(400,10)); 127 this.add(message, new GridBagConstraints(0, 6, 3, 1, 0.0, 0.0, 128 GridBagConstraints.LINE_START, GridBagConstraints.NONE, 129 new Insets(10, 10, 10, 10), 5, 5)); 130 131 message.setText(" "); 132 okayButton.setText(Bundle.getMessage("AssociateOkay")); 133 okayButton.addActionListener(this); 134 okayButton.setEnabled(false); 135 this.add(okayButton, new GridBagConstraints(0, 7, 1, 1, 0.0, 0.0, 136 GridBagConstraints.LINE_START, GridBagConstraints.NONE, 137 new Insets(20, 20, 20, 20), 10, 10)); 138 cancelButton.setText(Bundle.getMessage("AssociateCancel")); 139 cancelButton.addActionListener(this); 140 this.add(cancelButton, new GridBagConstraints(2, 7, 1, 1, 0.0, 0.0, 141 GridBagConstraints.LINE_END, GridBagConstraints.NONE, 142 new Insets(20, 20, 20, 20), 10, 10)); 143 setRoads(); 144 if (includeCars.isSelected()) { 145 if (roadsWith.size() < 1) { 146 message.setText(Bundle.getMessage("AssociateNoCars")); 147 } 148 } 149 if (roadList.size() < 1 ) { 150 message.setText(Bundle.getMessage("AssociateNoRoads")); 151 } 152 } 153 154 private void initRoads() { 155 log.debug("building a list of road number with their cars"); 156 roadList.clear(); 157 roadNumbers.clear(); 158 roadsWith.clear(); 159 numbersWith.clear(); 160 List<Car> carList = carManager.getList(); 161 for (Car thisCar : carList) { 162 if (numbersWith.containsKey(thisCar.getRoadName())) { 163 numbersWith.get(thisCar.getRoadName()).add(thisCar.getNumber()); 164 } else { 165 roadsWith.add(thisCar.getRoadName()); 166 ArrayList<String> tempArray = new ArrayList<>(); 167 tempArray.add(thisCar.getNumber()); 168 numbersWith.put(thisCar.getRoadName(), tempArray); 169 } 170 if ( "".equals(thisCar.getRfid()) ) { 171 if (roadNumbers.containsKey(thisCar.getRoadName())) { 172 roadNumbers.get(thisCar.getRoadName()).add(thisCar.getNumber()); 173 } else { 174 roadList.add(thisCar.getRoadName()); 175 ArrayList<String> tempArray = new ArrayList<>(); 176 tempArray.add(thisCar.getNumber()); 177 roadNumbers.put(thisCar.getRoadName(), tempArray); 178 } 179 } 180 } 181 java.util.Collections.sort(roadsWith); 182 java.util.Collections.sort(roadList); 183 for (String road : roadList) { 184 java.util.Collections.sort(roadNumbers.get(road), new AlphanumComparator()); 185 } 186 for (String road : roadsWith) { 187 java.util.Collections.sort(numbersWith.get(road), new AlphanumComparator()); 188 } 189 } 190 191 private void setOneRoad() { 192 message.setText(Bundle.getMessage("AssociateReady")); 193 numberCombo.setEnabled(true); 194 roadCombo.setSelectedIndex(0); 195 } 196 197 private void setRoads() { 198 roadCombo.removeListSelectionListener(this); 199 numberCombo.removeListSelectionListener(this); 200 message.setText(" "); 201 numberListModel.clear(); // no numbers until we have a selected road 202 okayButton.setEnabled(false); // can't okay until we have selected both 203 roadListModel.clear(); 204 List<String> theList; 205 if (includeCars.isSelected()) { 206 theList = roadsWith; 207 } else { 208 theList = roadList; 209 } 210 for (String road : theList) { 211 roadListModel.addElement(road); 212 } 213 roadCombo.addListSelectionListener(this); 214 if (theList.size() == 1) { 215 // we have only 1 item select it 216 setOneRoad(); 217 218 } 219 } 220 221 private void closePage() { 222 dispose(); 223 parentFrame.dispose(); 224 } 225 226 @Override 227 public void actionPerformed(ActionEvent e) { 228 if (e.getSource().equals(okayButton)) { 229 String thisRoad = roadCombo.getSelectedValue(); 230 String thisNumber = numberCombo.getSelectedValue(); 231 if (thisRoad == null || thisNumber == null) { 232 message.setText(Bundle.getMessage("AssociateNeedSelected")); 233 return; 234 } 235 Car thisCar = carManager.getByRoadAndNumber(thisRoad, thisNumber); 236 if (thisCar == null) { 237 log.error("Car was pulled from combo but was not found {} {}", thisRoad, thisNumber); 238 return; 239 } 240 if (e.getSource().equals(okayButton)) { 241 log.debug("setting this tag ({}) to car {} with number {}", tag, thisRoad, thisNumber); 242 tagManager.provideIdTag(tag); 243 thisCar.setRfid(tag); 244 closePage(); 245 } 246 } else if (e.getSource().equals(cancelButton)){ 247 log.debug("closing the Associate panel"); 248 closePage(); 249 } else if (e.getSource().equals(includeCars)) { 250 log.debug("the includeCars checkbox is changing - rebuilding lists"); 251 setRoads(); 252 253 } else { 254 log.error("action performed for an unrecognized source"); 255 return; 256 } 257 258 } 259 260 private void doNumbers(String road) { 261 numberCombo.removeListSelectionListener(this); 262 numberListModel.clear(); 263 List<String> numberList; 264 if (includeCars.isSelected()) { 265 numberList = numbersWith.get(road); 266 } else { 267 numberList = roadNumbers.get(road); 268 } 269 if (numberList == null) { 270 log.error("didn't find the road in the list"); 271 return; 272 } 273 for (String thisNumber : numberList) { 274 numberListModel.addElement(thisNumber); 275 } 276 numberCombo.addListSelectionListener(this); 277 numberCombo.setEnabled(true); 278 if (numberList.size() == 1) { 279 numberCombo.setSelectedIndex(0); 280 message.setText(Bundle.getMessage("AssociateOkayReady")); 281 } else { 282 message.setText(" "); 283 } 284 285 } 286 287 288 @Override 289 public void valueChanged(ListSelectionEvent e) { 290 log.debug("got a list selection event"); 291 if (e.getValueIsAdjusting()) { 292 return; 293 } 294 if (e.getSource().equals(roadCombo)) { 295 numberCombo.setEnabled(false); 296 numberCombo.removeListSelectionListener(this); 297 okayButton.setEnabled(false); 298 if (roadCombo.getSelectedIndex() == -1) { 299 log.debug("no selection - turning off numbers combo"); 300 message.setText(" "); 301 okayButton.setEnabled(false); 302 numberCombo.setEnabled(false); 303 numberListModel.clear(); 304 } else { 305 doNumbers(roadCombo.getSelectedValue()); 306 message.setText(Bundle.getMessage("AssociateReady")); 307 numberCombo.addListSelectionListener(this); 308 numberCombo.setEnabled(true); 309 } 310 } else if (e.getSource().equals(numberCombo)) { 311 if (numberCombo.getSelectedIndex() == -1 ) { 312 log.debug("road number was deselected - turning off okay button"); 313 message.setText(Bundle.getMessage("AssociateReady")); 314 okayButton.setEnabled(false); 315 } { 316 okayButton.setEnabled(true); 317 message.setText(Bundle.getMessage("AssociateOkayReady")); 318 } 319 } else { 320 log.error("don't recognize the source of the event"); 321 } 322 } 323 324}