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 if (track.getLoadNames().length == 0) { 335 track.setLoadOption(Track.ALL_LOADS); 336 } 337 // need to check if load configuration is to exclude all car types using this load 338 if (!track.isLoadNameAccepted(load)) { 339 JmriJOptionPane.showMessageDialog(this, 340 Bundle.getMessage("WarningExcludeTrackLoad", track.getLocation().getName(), 341 track.getName(), load), 342 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 343 needLoadTrackEditFrame = true; 344 } else if (!track.isLoadNameAndCarTypeAccepted(load, type)) { 345 JmriJOptionPane.showMessageDialog(this, 346 Bundle.getMessage("WarningExcludeTrackTypeAndLoad", 347 track.getLocation().getName(), track.getName(), type, load, 348 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 349 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 350 needLoadTrackEditFrame = true; 351 } 352 } 353 } else { 354 if (track.getLoadOption().equals(Track.INCLUDE_LOADS)) { 355 track.deleteLoadName(loadName); 356 // need to check if load configuration is to accept all car types using this load 357 if (track.isLoadNameAccepted(load)) { 358 JmriJOptionPane.showMessageDialog(this, 359 Bundle.getMessage("WarningAcceptTrackLoad", track.getLocation().getName(), 360 track.getName(), load), 361 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 362 needLoadTrackEditFrame = true; 363 } else if (track.isLoadNameAndCarTypeAccepted(load, type)) { 364 JmriJOptionPane.showMessageDialog(this, 365 Bundle.getMessage("WarningAcceptTrackTypeAndLoad", 366 track.getLocation().getName(), track.getName(), type, load, 367 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 368 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 369 needLoadTrackEditFrame = true; 370 } 371 } else if (track.getLoadOption().equals(Track.EXCLUDE_LOADS)) { 372 track.addLoadName(loadName); 373 } else if (track.getLoadOption().equals(Track.ALL_LOADS)) { 374 // need to exclude this load 375 track.setLoadOption(Track.EXCLUDE_LOADS); 376 track.addLoadName(loadName); 377 } 378 } 379 } 380 if (locId[1].equals(SHIP)) { 381 if (cb.isSelected()) { 382 if (track.getShipLoadOption().equals(Track.ALL_LOADS)) { 383 log.debug("Ship all loads selected for track ({})", track.getName()); 384 } else if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) { 385 track.addShipLoadName(loadName); 386 } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) { 387 track.deleteShipLoadName(loadName); 388 if (track.getShipLoadNames().length == 0) { 389 track.setShipLoadOption(Track.ALL_LOADS); 390 } 391 // need to check if load configuration is to exclude all car types using this load 392 if (!track.isLoadNameShipped(load)) { 393 JmriJOptionPane.showMessageDialog(this, 394 Bundle.getMessage("WarningExcludeTrackShipLoad", track.getLocation().getName(), 395 track.getName(), load), 396 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 397 needLoadTrackEditFrame = true; 398 } else if (!track.isLoadNameAndCarTypeShipped(load, type)) { 399 JmriJOptionPane.showMessageDialog(this, 400 Bundle.getMessage("WarningExcludeTrackShipTypeAndLoad", 401 track.getLocation().getName(), track.getName(), type, load, 402 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 403 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 404 needLoadTrackEditFrame = true; 405 } 406 } 407 } else { 408 if (track.getShipLoadOption().equals(Track.INCLUDE_LOADS)) { 409 track.deleteShipLoadName(loadName); 410 // need to check if load configuration is to accept all car types using this load 411 if (track.isLoadNameShipped(load)) { 412 JmriJOptionPane.showMessageDialog(this, 413 Bundle.getMessage("WarningShipTrackLoad", track.getLocation().getName(), 414 track.getName(), load), 415 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 416 needLoadTrackEditFrame = true; 417 } else if (track.isLoadNameAndCarTypeShipped(load, type)) { 418 JmriJOptionPane.showMessageDialog(this, 419 Bundle.getMessage("WarningShipTrackTypeAndLoad", track.getLocation().getName(), 420 track.getName(), type, load, 421 NEW_LINE + type + CarLoad.SPLIT_CHAR + load), 422 Bundle.getMessage("WarningTitle"), JmriJOptionPane.WARNING_MESSAGE); 423 needLoadTrackEditFrame = true; 424 } 425 } else if (track.getShipLoadOption().equals(Track.EXCLUDE_LOADS)) { 426 track.addShipLoadName(loadName); 427 } else if (track.getShipLoadOption().equals(Track.ALL_LOADS)) { 428 // need to exclude this load 429 track.setShipLoadOption(Track.EXCLUDE_LOADS); 430 track.addShipLoadName(loadName); 431 } 432 } 433 } 434 if (needLoadTrackEditFrame) { 435 if (tlef != null) { 436 tlef.dispose(); 437 } 438 tlef = new TrackLoadEditFrame(); 439 tlef.initComponents(track.getLocation(), track); 440 } 441 } 442 } 443 } 444 445 private void removePropertyChangeLocations() { 446 for (Location location : locationManager.getList()) { 447 location.removePropertyChangeListener(this); 448 List<Track> tracks = location.getTracksList(); 449 for (Track track : tracks) { 450 track.removePropertyChangeListener(this); 451 } 452 } 453 } 454 455 @Override 456 public void dispose() { 457 locationManager.removePropertyChangeListener(this); 458 InstanceManager.getDefault(CarTypes.class).removePropertyChangeListener(this); 459 InstanceManager.getDefault(CarLoads.class).removePropertyChangeListener(this); 460 removePropertyChangeLocations(); 461 super.dispose(); 462 } 463 464 @Override 465 public void propertyChange(java.beans.PropertyChangeEvent e) { 466 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), e 467 .getNewValue()); 468 if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY) || 469 e.getPropertyName().equals(Location.TYPES_CHANGED_PROPERTY) || 470 e.getPropertyName().equals(Location.NAME_CHANGED_PROPERTY) || 471 e.getPropertyName().equals(Location.TRACK_LISTLENGTH_CHANGED_PROPERTY) || 472 e.getPropertyName().equals(Track.TYPES_CHANGED_PROPERTY) || 473 e.getPropertyName().equals(Track.NAME_CHANGED_PROPERTY)) { 474 updateLocations(); 475 } 476 if (e.getPropertyName().equals(CarTypes.CARTYPES_CHANGED_PROPERTY) || 477 e.getPropertyName().equals(CarTypes.CARTYPES_NAME_CHANGED_PROPERTY)) { 478 updateTypeComboBox(); 479 } 480 if (e.getPropertyName().equals(CarLoads.LOAD_CHANGED_PROPERTY)) { 481 updateLoadComboBox(); 482 } 483 if (e.getPropertyName().equals(Track.LOADS_CHANGED_PROPERTY) && e.getSource().getClass().equals(Track.class)) { 484 Track track = (Track) e.getSource(); 485 updateLoadStatusCheckBox(track); 486 } 487 } 488 489 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(LocationsByCarLoadFrame.class); 490}