001package jmri.jmrit.operations.locations.tools; 002 003import java.awt.Dimension; 004import java.awt.GridBagLayout; 005import java.util.ArrayList; 006import java.util.List; 007 008import javax.swing.*; 009 010import jmri.InstanceManager; 011import jmri.jmrit.operations.OperationsFrame; 012import jmri.jmrit.operations.OperationsXml; 013import jmri.jmrit.operations.locations.*; 014import jmri.jmrit.operations.rollingstock.cars.*; 015import jmri.jmrit.operations.setup.Control; 016import jmri.jmrit.operations.setup.Setup; 017import jmri.util.swing.JmriJOptionPane; 018 019/** 020 * Frame to display which locations service certain car loads 021 * 022 * @author Dan Boudreau Copyright (C) 2014 023 */ 024public class LocationsByCarLoadFrame extends OperationsFrame implements java.beans.PropertyChangeListener { 025 026 private final static String RECEIVE = "r"; 027 private final static String SHIP = "s"; 028 private final static String HYPHEN = "-"; 029 030 LocationManager locationManager; 031 032 // checkboxes track id as the checkbox name 033 ArrayList<JCheckBox> trackCheckBoxList = new ArrayList<>(); 034 JPanel locationCheckBoxes = new JPanel(); 035 036 // panels 037 JPanel pLocations; 038 039 // major buttons 040 JButton clearButton = new JButton(Bundle.getMessage("ClearAll")); 041 JButton setButton = new JButton(Bundle.getMessage("SelectAll")); 042 JButton saveButton = new JButton(Bundle.getMessage("ButtonSave")); 043 044 // check boxes 045 JCheckBox loadAndTypeCheckBox = new JCheckBox(Bundle.getMessage("TypeAndLoad")); 046 047 // radio buttons 048 // text field 049 // combo boxes 050 JComboBox<String> typeComboBox = InstanceManager.getDefault(CarTypes.class).getComboBox(); 051 JComboBox<String> loadComboBox = InstanceManager.getDefault(CarLoads.class).getComboBox(null); 052 053 // selected location 054 Location _location; 055 056 public LocationsByCarLoadFrame() { 057 super(); 058 } 059 060 public void initComponents(Location location) { 061 this._location = location; 062 initComponents(); 063 } 064 065 @Override 066 public void initComponents() { 067 068 // load managers 069 locationManager = InstanceManager.getDefault(LocationManager.class); 070 071 // general GUI config 072 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 073 074 // Set up the panels 075 JPanel pCarType = new JPanel(); 076 pCarType.setLayout(new GridBagLayout()); 077 pCarType.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Type"))); 078 079 addItem(pCarType, typeComboBox, 0, 0); 080 081 JPanel pLoad = new JPanel(); 082 pLoad.setLayout(new GridBagLayout()); 083 pLoad.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Load"))); 084 085 addItem(pLoad, loadComboBox, 0, 0); 086 addItem(pLoad, loadAndTypeCheckBox, 1, 0); 087 088 pLocations = new JPanel(); 089 pLocations.setLayout(new GridBagLayout()); 090 JScrollPane locationPane = new JScrollPane(pLocations); 091 locationPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 092 locationPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Locations"))); 093 updateLoadComboBox(); 094 095 JPanel pButtons = new JPanel(); 096 pButtons.setLayout(new GridBagLayout()); 097 pButtons.setBorder(BorderFactory.createTitledBorder("")); 098 099 addItem(pButtons, clearButton, 0, 0); 100 addItem(pButtons, setButton, 1, 0); 101 addItem(pButtons, saveButton, 2, 0); 102 103 getContentPane().add(pCarType); 104 getContentPane().add(pLoad); 105 getContentPane().add(locationPane); 106 getContentPane().add(pButtons); 107 108 // setup combo box 109 addComboBoxAction(typeComboBox); 110 addComboBoxAction(loadComboBox); 111 112 // setup buttons 113 addButtonAction(setButton); 114 addButtonAction(clearButton); 115 addButtonAction(saveButton); 116 117 // setup checkbox 118 addCheckBoxAction(loadAndTypeCheckBox); 119 120 locationManager.addPropertyChangeListener(this); 121 InstanceManager.getDefault(CarTypes.class).addPropertyChangeListener(this); 122 InstanceManager.getDefault(CarLoads.class).addPropertyChangeListener(this); 123 124 // build menu 125 JMenuBar menuBar = new JMenuBar(); 126 JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools")); 127 toolMenu.add(new PrintLocationsByCarTypesAction(false)); 128 toolMenu.add(new PrintLocationsByCarTypesAction(true)); 129 menuBar.add(toolMenu); 130 setJMenuBar(menuBar); 131 addHelpMenu("package.jmri.jmrit.operations.Operations_ModifyLocationsByCarType", true); // NOI18N 132 133 setPreferredSize(null); // we need to resize this frame 134 pack(); 135 setMinimumSize(new Dimension(Control.panelWidth300, Control.panelHeight250)); 136 setSize(getWidth() + 25, getHeight()); // make a bit wider to eliminate scroll bar 137 if (_location != null) { 138 setTitle(Bundle.getMessage("TitleModifyLocationLoad")); 139 } else { 140 setTitle(Bundle.getMessage("TitleModifyLocationsLoad")); 141 } 142 setVisible(true); 143 } 144 145 @Override 146 public void comboBoxActionPerformed(java.awt.event.ActionEvent ae) { 147 log.debug("combo box action"); 148 if (ae.getSource() == loadComboBox) { 149 log.debug("Load combobox change, selected load: ({})", loadComboBox.getSelectedItem()); 150 if (loadComboBox.isEnabled() && loadComboBox.getSelectedItem() != null) { 151 updateLocations(); 152 } 153 } 154 if (ae.getSource() == typeComboBox) { 155 updateLoadComboBox(); 156 } 157 } 158 159 // Save, Delete, Add 160 @Override 161 public void buttonActionPerformed(java.awt.event.ActionEvent ae) { 162 if (ae.getSource() == saveButton) { 163 save(); 164 } 165 if (ae.getSource() == setButton) { 166 selectCheckboxes(true); 167 } 168 if (ae.getSource() == clearButton) { 169 selectCheckboxes(false); 170 } 171 } 172 173 /** 174 * Update the car types that locations and tracks service. Note that the 175 * checkbox name is the id of the location or track. 176 */ 177 private void save() { 178 log.debug("save"); 179 OperationsXml.save(); 180 if (Setup.isCloseWindowOnSaveEnabled()) { 181 dispose(); 182 } 183 } 184 185 /** 186 * Update tracks at locations based on car type serviced, or car loads 187 * serviced. If car loads, disable any tracks that don't service the car 188 * type selected. 189 */ 190 private void updateLocations() { 191 log.debug("update checkboxes"); 192 removePropertyChangeLocations(); 193 trackCheckBoxList.clear(); 194 int x = 0; 195 pLocations.removeAll(); 196 String type = (String) typeComboBox.getSelectedItem(); 197 String load = (String) loadComboBox.getSelectedItem(); 198 log.debug("Selected car type : ({}) load ({})", type, load); 199 // did the location get deleted? 200 if (_location != null && locationManager.getLocationByName(_location.getName()) == null) { 201 _location = null; 202 } 203 List<Location> locations = locationManager.getLocationsByNameList(); 204 for (Location location : locations) { 205 // show only one location? 206 if (_location != null && _location != location) { 207 continue; 208 } 209 location.addPropertyChangeListener(this); 210 JLabel locationName = new JLabel(location.getName()); 211 addItemLeft(pLocations, locationName, 0, x++); 212 List<Track> tracks = location.getTracksByNameList(null); 213 for (Track track : tracks) { 214 track.addPropertyChangeListener(this); 215 JCheckBox cb = new JCheckBox(track.getName()); 216 cb.setName(track.getId() + HYPHEN + RECEIVE); 217 addCheckBoxAction(cb); 218 trackCheckBoxList.add(cb); 219 cb.setEnabled(track.isTypeNameAccepted(type)); 220 cb.setSelected(track.isLoadNameAndCarTypeAccepted(load, type)); 221 addItemLeft(pLocations, cb, 1, x++); 222 if (cb.isEnabled()) { 223 cb.setToolTipText(Bundle.getMessage("TipTrackCarLoad", load)); 224 } else { 225 cb.setToolTipText(Bundle.getMessage("TipTrackNotThisType", type)); 226 } 227 } 228 if (location.isStaging()) { 229 JLabel ships = new JLabel(location.getName() + " (" + Bundle.getMessage("Ships") + ")"); 230 addItemLeft(pLocations, ships, 0, x++); 231 for (Track track : tracks) { 232 JCheckBox cb = new JCheckBox(track.getName()); 233 cb.setName(track.getId() + HYPHEN + SHIP); 234 addCheckBoxAction(cb); 235 trackCheckBoxList.add(cb); 236 cb.setEnabled(track.isTypeNameAccepted(type)); 237 cb.setSelected(track.isLoadNameAndCarTypeShipped(load, type)); 238 addItemLeft(pLocations, cb, 1, x++); 239 if (cb.isEnabled()) { 240 cb.setToolTipText(Bundle.getMessage("TipTrackCarShipsLoad", load)); 241 } else { 242 cb.setToolTipText(Bundle.getMessage("TipTrackNotThisType", type)); 243 } 244 } 245 246 } 247 } 248 pLocations.revalidate(); 249 repaint(); 250 } 251 252 private void updateTypeComboBox() { 253 log.debug("update type combobox"); 254 InstanceManager.getDefault(CarTypes.class).updateComboBox(typeComboBox); 255 } 256 257 private void updateLoadComboBox() { 258 log.debug("update load combobox"); 259 if (typeComboBox.getSelectedItem() != null) { 260 String type = (String) typeComboBox.getSelectedItem(); 261 String load = (String) loadComboBox.getSelectedItem(); 262 log.debug("Selected car type : ({}) load ({})", type, load); 263 InstanceManager.getDefault(CarLoads.class).updateComboBox(type, loadComboBox); 264 loadComboBox.setEnabled(false); // used as a flag to prevent updateLocations() 265 if (load != null) { 266 loadComboBox.setSelectedItem(load); 267 } 268 loadComboBox.setEnabled(true); 269 updateLocations(); 270 } 271 } 272 273 private void updateLoadStatusCheckBox(Track track) { 274 String type = (String) typeComboBox.getSelectedItem(); 275 String load = (String) loadComboBox.getSelectedItem(); 276 log.debug("Update load status for track ({}, {})", track.getLocation().getName(), track.getName()); 277 for (JCheckBox cb : trackCheckBoxList) { 278 if (cb.getName().split(HYPHEN)[0].equals(track.getId())) { 279 if (cb.getName().split(HYPHEN)[1].equals(RECEIVE)) { 280 cb.setSelected(track.isLoadNameAndCarTypeAccepted(load, type)); 281 } else { 282 cb.setSelected(track.isLoadNameAndCarTypeShipped(load, type)); 283 } 284 } 285 } 286 } 287 288 private void selectCheckboxes(boolean select) { 289 for (JCheckBox cb : trackCheckBoxList) { 290 if (cb.isEnabled()) { 291 cb.setSelected(select); 292 checkBoxUpdate(cb); 293 } 294 } 295 } 296 297 TrackLoadEditFrame tlef; // if there's an issue bring up the load edit window 298 299 @Override 300 public void checkBoxActionPerformed(java.awt.event.ActionEvent ae) { 301 if (ae.getSource() == loadAndTypeCheckBox) { 302 updateLocations(); 303 return; 304 } 305 JCheckBox cb = (JCheckBox) ae.getSource(); 306 checkBoxUpdate(cb); 307 } 308 309 private void checkBoxUpdate(JCheckBox cb) { 310 String[] locId = cb.getName().split(HYPHEN); 311 Location loc = locationManager.getLocationById(locId[0].split(Location.LOC_TRACK_REGIX)[0]); 312 if (loc != null) { 313 Track track = loc.getTrackById(locId[0]); 314 // if enabled track services this car type 315 log.debug("CheckBox : {} track: ({}) isEnabled: {} isSelected: {}", cb.getName(), track.getName(), cb 316 .isEnabled(), cb.isSelected()); 317 if (cb.isEnabled()) { 318 boolean needLoadTrackEditFrame = false; 319 String loadName = (String) loadComboBox.getSelectedItem(); 320 String load = loadName; 321 String type = (String) typeComboBox.getSelectedItem(); 322 log.debug("Selected load ({})", loadName); 323 if (loadAndTypeCheckBox.isSelected()) { 324 loadName = type + CarLoad.SPLIT_CHAR + loadName; 325 } 326 if (locId[1].equals(RECEIVE)) { 327 if (cb.isSelected()) { 328 if (track.getLoadOption().equals(Track.ALL_LOADS)) { 329 log.debug("All loads selected for track ({})", track.getName()); 330 } else if (track.getLoadOption().equals(Track.INCLUDE_LOADS)) { 331 track.addLoadName(loadName); 332 } else if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) { 333 track.deleteLoadName(loadName); 334 // need to check if load configuration is to exclude all car types using this load 335 if (!track.isLoadNameAccepted(load)) { 336 JmriJOptionPane.showMessageDialog(this, 337 Bundle.getMessage("WarningExcludeTrackLoad", track.getLocation().getName(), 338 track.getName(), load), 339 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 340 needLoadTrackEditFrame = true; 341 } else if (!track.isLoadNameAndCarTypeAccepted(load, type)) { 342 JmriJOptionPane.showMessageDialog(this, 343 Bundle.getMessage("WarningExcludeTrackTypeAndLoad", 344 track.getLocation().getName(), track.getName(), type, load, 345 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 346 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 347 needLoadTrackEditFrame = true; 348 } 349 } 350 } else { 351 if (track.getLoadOption().equals(Track.INCLUDE_LOADS)) { 352 track.deleteLoadName(loadName); 353 // need to check if load configuration is to accept all car types using this load 354 if (track.isLoadNameAccepted(load)) { 355 JmriJOptionPane.showMessageDialog(this, 356 Bundle.getMessage("WarningAcceptTrackLoad", track.getLocation().getName(), 357 track.getName(), load), 358 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 359 needLoadTrackEditFrame = true; 360 } else if (track.isLoadNameAndCarTypeAccepted(load, type)) { 361 JmriJOptionPane.showMessageDialog(this, 362 Bundle.getMessage("WarningAcceptTrackTypeAndLoad", 363 track.getLocation().getName(), track.getName(), type, load, 364 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 365 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 366 needLoadTrackEditFrame = true; 367 } 368 } else if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) { 369 track.addLoadName(loadName); 370 } else if (track.getLoadOption().equals(Track.ALL_LOADS)) { 371 // need to exclude this load 372 track.setLoadOption(Track.EXCLUDE_LOADS); 373 track.addLoadName(loadName); 374 } 375 } 376 } 377 if (locId[1].equals(SHIP)) { 378 if (cb.isSelected()) { 379 if (track.getShipLoadOption().equals(Track.ALL_LOADS)) { 380 log.debug("Ship all loads selected for track ({})", track.getName()); 381 } else if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) { 382 track.addShipLoadName(loadName); 383 } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) { 384 track.deleteShipLoadName(loadName); 385 // need to check if load configuration is to exclude all car types using this load 386 if (!track.isLoadNameShipped(load)) { 387 JmriJOptionPane.showMessageDialog(this, 388 Bundle.getMessage("WarningExcludeTrackShipLoad", track.getLocation().getName(), 389 track.getName(), load), 390 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 391 needLoadTrackEditFrame = true; 392 } else if (!track.isLoadNameAndCarTypeShipped(load, type)) { 393 JmriJOptionPane.showMessageDialog(this, 394 Bundle.getMessage("WarningExcludeTrackShipTypeAndLoad", 395 track.getLocation().getName(), track.getName(), type, load, 396 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 397 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 398 needLoadTrackEditFrame = true; 399 } 400 } 401 } else { 402 if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) { 403 track.deleteShipLoadName(loadName); 404 // need to check if load configuration is to accept all car types using this load 405 if (track.isLoadNameShipped(load)) { 406 JmriJOptionPane.showMessageDialog(this, 407 Bundle.getMessage("WarningShipTrackLoad", track.getLocation().getName(), 408 track.getName(), load), 409 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 410 needLoadTrackEditFrame = true; 411 } else if (track.isLoadNameAndCarTypeShipped(load, type)) { 412 JmriJOptionPane.showMessageDialog(this, 413 Bundle.getMessage("WarningShipTrackTypeAndLoad", track.getLocation().getName(), 414 track.getName(), type, load, 415 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 416 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 417 needLoadTrackEditFrame = true; 418 } 419 } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) { 420 track.addShipLoadName(loadName); 421 } else if (track.getShipLoadOption().equals(Track.ALL_LOADS)) { 422 // need to exclude this load 423 track.setShipLoadOption(Track.EXCLUDE_LOADS); 424 track.addShipLoadName(loadName); 425 } 426 } 427 } 428 if (needLoadTrackEditFrame) { 429 if (tlef != null) { 430 tlef.dispose(); 431 } 432 tlef = new TrackLoadEditFrame(); 433 tlef.initComponents(track.getLocation(), track); 434 } 435 } 436 } 437 } 438 439 private void removePropertyChangeLocations() { 440 for (Location location : locationManager.getList()) { 441 location.removePropertyChangeListener(this); 442 List<Track> tracks = location.getTracksList(); 443 for (Track track : tracks) { 444 track.removePropertyChangeListener(this); 445 } 446 } 447 } 448 449 @Override 450 public void dispose() { 451 locationManager.removePropertyChangeListener(this); 452 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 453 InstanceManager.getDefault(CarLoads.class).removePropertyChangeListener(this); 454 removePropertyChangeLocations(); 455 super.dispose(); 456 } 457 458 @Override 459 public void propertyChange(java.beans.PropertyChangeEvent e) { 460 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 461 .getNewValue()); 462 if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) || 463 e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) || 464 e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY) || 465 e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) || 466 e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY) || 467 e.getPropertyName().equals(Track.NAME_CHANGED_PROPERTY)) { 468 updateLocations(); 469 } 470 if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 471 e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) { 472 updateTypeComboBox(); 473 } 474 if (e.getPropertyName().equals(CarLoads.LOAD_CHANGED_PROPERTY)) { 475 updateLoadComboBox(); 476 } 477 if (e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) && e.getSource().getClass().equals(Track.class)) { 478 Track track = (Track) e.getSource(); 479 updateLoadStatusCheckBox(track); 480 } 481 } 482 483 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocationsByCarLoadFrame.class); 484}