/** * Common elements for the Conductor and Yardmaster Frames. * * @author Dan Boudreau Copyright (C) 2013 * @version $Revision: 18630 $ */ public abstract class CommonConductorYardmasterPanel extends OperationsPanel implements PropertyChangeListener { protected static final String Tab = " "; // used to space out headers protected static final String Space = " "; // used to pad out panels protected Location _location = null; protected Train _train = null; protected TrainManager trainManager = TrainManager.instance(); protected EngineManager engManager = EngineManager.instance(); protected CarManager carManager = CarManager.instance(); protected TrainCommon trainCommon = new TrainCommon(); protected JScrollPane locoPane; protected JScrollPane pickupPane; protected JScrollPane setoutPane; protected JScrollPane movePane; // labels protected JLabel textRailRoadName = new JLabel(); protected JLabel textTrainDescription = new JLabel(); protected JLabel textLocationName = new JLabel(); protected JLabel textStatus = new JLabel(); // major buttons protected JButton selectButton = new JButton(Bundle.getMessage("Select")); protected JButton clearButton = new JButton(Bundle.getMessage("Clear")); protected JButton setButton = new JButton(Bundle.getMessage("Set")); protected JButton moveButton = new JButton(Bundle.getMessage("Move")); // text panes protected JTextPane textLocationCommentPane = new JTextPane(); protected JTextPane textTrainCommentPane = new JTextPane(); protected JTextPane textTrainRouteCommentPane = new JTextPane(); protected JTextPane textTrainRouteLocationCommentPane = new JTextPane(); // panels protected JPanel pRailRoadName = new JPanel(); protected JPanel pTrainDescription = new JPanel(); protected JPanel pLocationName = new JPanel(); protected JPanel pLocos = new JPanel(); protected JPanel pPickupLocos = new JPanel(); protected JPanel pSetoutLocos = new JPanel(); protected JPanel pPickups = new JPanel(); protected JPanel pSetouts = new JPanel(); protected JPanel pWorkPanes = new JPanel(); // place car pick ups and set outs side by side using two columns protected JPanel pMoves = new JPanel(); protected JPanel pStatus = new JPanel(); protected JPanel pButtons = new JPanel(); // check boxes protected Hashtable<String, JCheckBox> checkBoxes = new Hashtable<>(); protected List<RollingStock> rollingStock = new ArrayList<>(); // flags protected boolean isSetMode = false; // when true, cars that aren't selected (checkbox) can be "set" public CommonConductorYardmasterPanel() { super(); } public void initComponents() { setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); locoPane = new JScrollPane(pLocos); locoPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Engines"))); pickupPane = new JScrollPane(pPickups); pickupPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Pickup"))); setoutPane = new JScrollPane(pSetouts); setoutPane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("SetOut"))); movePane = new JScrollPane(pMoves); movePane.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("LocalMoves"))); // Set up the panels pPickupLocos.setLayout(new BoxLayout(pPickupLocos, BoxLayout.Y_AXIS)); pSetoutLocos.setLayout(new BoxLayout(pSetoutLocos, BoxLayout.Y_AXIS)); pPickups.setLayout(new BoxLayout(pPickups, BoxLayout.Y_AXIS)); pSetouts.setLayout(new BoxLayout(pSetouts, BoxLayout.Y_AXIS)); pMoves.setLayout(new BoxLayout(pMoves, BoxLayout.Y_AXIS)); // railroad name pRailRoadName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("RailroadName"))); pRailRoadName.add(textRailRoadName); // location name pLocationName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location"))); pLocationName.add(textLocationName); // location comment textLocationCommentPane.setBorder( BorderFactory.createTitledBorder(Bundle.getMessage("LocationComment"))); textLocationCommentPane.setBackground(null); textLocationCommentPane.setEditable(false); textLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); // train description pTrainDescription.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Description"))); pTrainDescription.add(textTrainDescription); // train comment textTrainCommentPane.setBorder( BorderFactory.createTitledBorder(Bundle.getMessage("TrainComment"))); textTrainCommentPane.setBackground(null); textTrainCommentPane.setEditable(false); textTrainCommentPane.setMaximumSize(new Dimension(2000, 200)); // train route comment textTrainRouteCommentPane.setBorder( BorderFactory.createTitledBorder(Bundle.getMessage("RouteComment"))); textTrainRouteCommentPane.setBackground(null); textTrainRouteCommentPane.setEditable(false); textTrainRouteCommentPane.setMaximumSize(new Dimension(2000, 200)); // train route location comment textTrainRouteLocationCommentPane.setBorder( BorderFactory.createTitledBorder(Bundle.getMessage("RouteLocationComment"))); textTrainRouteLocationCommentPane.setBackground(null); textTrainRouteLocationCommentPane.setEditable(false); textTrainRouteLocationCommentPane.setMaximumSize(new Dimension(2000, 200)); // row 12 if ((getPreferredSize().width > Control.panelWidth1025 && Setup.isTabEnabled()) || (getPreferredSize().width > Control.panelWidth1025 - 200 && !Setup.isTabEnabled())) { pLocos.setLayout(new BoxLayout(pLocos, BoxLayout.X_AXIS)); pWorkPanes.setLayout(new BoxLayout(pWorkPanes, BoxLayout.X_AXIS)); } else { pLocos.setLayout(new BoxLayout(pLocos, BoxLayout.Y_AXIS)); pWorkPanes.setLayout(new BoxLayout(pWorkPanes, BoxLayout.Y_AXIS)); } pLocos.add(pPickupLocos); pLocos.add(pSetoutLocos); pWorkPanes.add(pickupPane); pWorkPanes.add(setoutPane); // row 13 pStatus.setLayout(new GridBagLayout()); pStatus.setBorder(BorderFactory.createTitledBorder("")); addItem(pStatus, textStatus, 0, 0); // row 14 pButtons.setLayout(new GridBagLayout()); pButtons.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Work"))); addItem(pButtons, selectButton, 0, 0); addItem(pButtons, clearButton, 1, 0); addItem(pButtons, setButton, 2, 0); // setup buttons addButtonAction(selectButton); addButtonAction(clearButton); addButtonAction(setButton); setMinimumSize(new Dimension(Control.panelWidth500, Control.panelHeight500)); } // Select, Clear, and Set Buttons @Override public void buttonActionPerformed(ActionEvent ae) { if (ae.getSource() == selectButton) { selectCheckboxes(true); } if (ae.getSource() == clearButton) { selectCheckboxes(false); } if (ae.getSource() == setButton) { isSetMode = !isSetMode; // toggle setMode update(); } check(); } protected void initialize() { removePropertyChangeListerners(); pPickupLocos.removeAll(); pSetoutLocos.removeAll(); pPickups.removeAll(); pSetouts.removeAll(); pMoves.removeAll(); // turn everything off and re-enable if needed pWorkPanes.setVisible(false); pickupPane.setVisible(false); setoutPane.setVisible(false); locoPane.setVisible(false); pPickupLocos.setVisible(false); pSetoutLocos.setVisible(false); movePane.setVisible(false); textTrainRouteLocationCommentPane.setVisible(false); setButtonText(); } protected void updateComplete() { pPickupLocos.repaint(); pSetoutLocos.repaint(); pPickups.repaint(); pSetouts.repaint(); pMoves.repaint(); pPickupLocos.revalidate(); pSetoutLocos.revalidate(); pPickups.revalidate(); pSetouts.revalidate(); pMoves.revalidate(); selectButton.setEnabled(checkBoxes.size() > 0 && !isSetMode); clearButton.setEnabled(checkBoxes.size() > 0 && !isSetMode); check(); log.debug("update complete"); } CarSetFrame csf = null; // action for set button for a car, opens the set car window public void carSetButtonActionPerfomed(ActionEvent ae) { String name = ((JButton) ae.getSource()).getName(); log.debug("Set button for car {}", name); Car car = carManager.getById(name); if (csf != null) { csf.dispose(); } csf = new CarSetFrame(); csf.initComponents(); csf.loadCar(car); } EngineSetFrame esf = null; // action for set button for an engine, opens the set engine window public void engineSetButtonActionPerfomed(ActionEvent ae) { String name = ((JButton) ae.getSource()).getName(); log.debug("Set button for loco {}", name); Engine eng = engManager.getById(name); if (esf != null) { esf.dispose(); } esf = new EngineSetFrame(); esf.initComponents(); esf.loadEngine(eng); } // confirm that all work is done @Override protected void checkBoxActionPerformed(ActionEvent ae) { check(); } // Determines if all car checkboxes are selected. Disables the Set button if // all checkbox are selected. protected void check() { Enumeration<JCheckBox> en = checkBoxes.elements(); while (en.hasMoreElements()) { JCheckBox checkBox = en.nextElement(); if (!checkBox.isSelected()) { // log.debug("Checkbox (" + checkBox.getText() + ") isn't selected "); moveButton.setEnabled(false); setButton.setEnabled(true); return; } } // all selected, work done! moveButton.setEnabled(_train != null && _train.isBuilt()); setButton.setEnabled(false); isSetMode = false; setButtonText(); } protected void selectCheckboxes(boolean enable) { Enumeration<JCheckBox> en = checkBoxes.elements(); while (en.hasMoreElements()) { JCheckBox checkBox = en.nextElement(); checkBox.setSelected(enable); } isSetMode = false; } /** * Uses "ep" prefix to denote a checkbox with an engine pick up, and "es" for an engine set out. * * @param rl The routeLocation to show loco pick ups or set outs. */ protected void updateLocoPanes(RouteLocation rl) { if (Setup.isPrintHeadersEnabled()) { JLabel header = new JLabel(Tab + trainCommon.getPickupEngineHeader()); setLabelFont(header); pPickupLocos.add(header); JLabel headerDrop = new JLabel(Tab + trainCommon.getDropEngineHeader()); setLabelFont(headerDrop); pSetoutLocos.add(headerDrop); } // check for locos List<Engine> engList = engManager.getByTrainBlockingList(_train); for (Engine engine : engList) { if (engine.getRouteLocation() == rl && engine.getTrack() != null) { locoPane.setVisible(true); pPickupLocos.setVisible(true); rollingStock.add(engine); engine.addPropertyChangeListener(this); JCheckBox checkBox; if (checkBoxes.containsKey("ep" + engine.getId())) { checkBox = checkBoxes.get("ep" + engine.getId()); } else { checkBox = new JCheckBox(trainCommon.pickupEngine(engine)); setCheckBoxFont(checkBox); addCheckBoxAction(checkBox); checkBoxes.put("ep" + engine.getId(), checkBox); } if (isSetMode && !checkBox.isSelected()) { pPickupLocos.add(addSet(engine)); } else { pPickupLocos.add(checkBox); } } if (engine.getRouteDestination() == rl) { locoPane.setVisible(true); pSetoutLocos.setVisible(true); rollingStock.add(engine); engine.addPropertyChangeListener(this); JCheckBox checkBox; if (checkBoxes.containsKey("es" + engine.getId())) { checkBox = checkBoxes.get("es" + engine.getId()); } else { checkBox = new JCheckBox(trainCommon.dropEngine(engine)); setCheckBoxFont(checkBox); addCheckBoxAction(checkBox); checkBoxes.put("es" + engine.getId(), checkBox); } if (isSetMode && !checkBox.isSelected()) { pSetoutLocos.add(addSet(engine)); } else { pSetoutLocos.add(checkBox); } } } // pad the panels in case the horizontal scroll bar appears pPickupLocos.add(new JLabel(Space)); pSetoutLocos.add(new JLabel(Space)); } /** * Block cars by track (optional), then pick up and set out for each location in a train's route. * This shows each car with a check box or with a set button. The set button is displayed when the * checkbox isn't selected and the display is in "set" mode. If the car is a utility. Show the * number of cars that have the same attributes, and not the car's road and number. Each car is * displayed only once in one of three panes. The three panes are pick up, set out, or local move. * To keep track of each car and which pane to use, they are placed in the list "rollingStock" * with the prefix "p", "s" or "m" and the car's unique id. */ protected void blockCars(RouteLocation rl, boolean isManifest) { if (Setup.isPrintHeadersEnabled()) { JLabel header = new JLabel( Tab + trainCommon.getPickupCarHeader(isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK)); setLabelFont(header); pPickups.add(header); header = new JLabel( Tab + trainCommon.getDropCarHeader(isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK)); setLabelFont(header); pSetouts.add(header); header = new JLabel(Tab + trainCommon.getLocalMoveHeader(isManifest)); setLabelFont(header); pMoves.add(header); } List<Track> tracks = rl.getLocation().getTrackByNameList(null); List<RouteLocation> routeList = _train.getRoute().getLocationsBySequenceList(); List<Car> carList = carManager.getByTrainDestinationList(_train); for (Track track : tracks) { for (RouteLocation rld : routeList) { for (Car car : carList) { // determine if car is a pick up from the right track // caboose or FRED is placed at end of the train // passenger trains are already blocked in the car list if (car.getTrack() != null && car.getRouteLocation() == rl && car.getRouteDestination() != rl && (!Setup.isSortByTrackEnabled() || car.getTrackName().equals(track.getName())) && ((car.getRouteDestination() == rld && !car.isCaboose() && !car.hasFred()) || (rld == routeList.get(routeList.size() - 1) && (car.isCaboose() || car.hasFred())) || car.isPassenger())) { // yes we have a pick up pWorkPanes.setVisible(true); pickupPane.setVisible(true); if (!rollingStock.contains(car)) { rollingStock.add(car); car.addPropertyChangeListener(this); } // did we already process this car? if (checkBoxes.containsKey("p" + car.getId())) { if (isSetMode && !checkBoxes.get("p" + car.getId()).isSelected()) { // change to set button so user can remove car from train pPickups.add(addSet(car)); } else { pPickups.add(checkBoxes.get("p" + car.getId())); } // figure out the checkbox text, either single car or utility } else { String text; if (car.isUtility()) { text = trainCommon.pickupUtilityCars( carList, car, isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK); if (text == null) { continue; // this car type has already been processed } } else { text = trainCommon.pickupCar(car, isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK); } JCheckBox checkBox = new JCheckBox(text); setCheckBoxFont(checkBox); addCheckBoxAction(checkBox); pPickups.add(checkBox); checkBoxes.put("p" + car.getId(), checkBox); } } } } // set outs and local moves for (Car car : carList) { if (car.getRouteDestination() != rl || car.getDestinationTrack() == null) { continue; } // car in train if track null, second check is for yard master window if (car.getTrack() == null || car.getTrack() != null && (car.getRouteLocation() != rl)) { if (Setup.isSortByTrackEnabled() && !car.getDestinationTrack().getName().equals(track.getName())) { continue; } // we have set outs pWorkPanes.setVisible(true); setoutPane.setVisible(true); if (!rollingStock.contains(car)) { rollingStock.add(car); car.addPropertyChangeListener(this); } if (checkBoxes.containsKey("s" + car.getId())) { if (isSetMode && !checkBoxes.get("s" + car.getId()).isSelected()) { // change to set button so user can remove car from train pSetouts.add(addSet(car)); } else { pSetouts.add(checkBoxes.get("s" + car.getId())); } } else { String text; if (car.isUtility()) { text = trainCommon.setoutUtilityCars(carList, car, !TrainCommon.LOCAL, isManifest); if (text == null) { continue; // this car type has already been processed } } else { text = trainCommon.dropCar(car, isManifest, !TrainCommon.IS_TWO_COLUMN_TRACK); } JCheckBox checkBox = new JCheckBox(text); setCheckBoxFont(checkBox); addCheckBoxAction(checkBox); pSetouts.add(checkBox); checkBoxes.put("s" + car.getId(), checkBox); } // local move? } else if (car.getTrack() != null && car.getRouteLocation() == rl && (!Setup.isSortByTrackEnabled() || car.getTrack().getName().equals(track.getName()))) { movePane.setVisible(true); if (!rollingStock.contains(car)) { rollingStock.add(car); car.addPropertyChangeListener(this); } if (checkBoxes.containsKey("m" + car.getId())) { if (isSetMode && !checkBoxes.get("m" + car.getId()).isSelected()) { // change to set button so user can remove car from train pMoves.add(addSet(car)); } else { pMoves.add(checkBoxes.get("m" + car.getId())); } } else { String text; if (car.isUtility()) { text = trainCommon.setoutUtilityCars(carList, car, TrainCommon.LOCAL, isManifest); if (text == null) { continue; // this car type has already been processed } } else { text = trainCommon.localMoveCar(car, isManifest); } JCheckBox checkBox = new JCheckBox(text); setCheckBoxFont(checkBox); addCheckBoxAction(checkBox); pMoves.add(checkBox); checkBoxes.put("m" + car.getId(), checkBox); } } } // if not sorting by track, we're done if (!Setup.isSortByTrackEnabled()) { break; } } // pad the panels in case the horizontal scroll bar appears pPickups.add(new JLabel(Space)); pSetouts.add(new JLabel(Space)); pMoves.add(new JLabel(Space)); } // replace the car or engine checkbox and text with only the road and number and a Set button protected JPanel addSet(RollingStock rs) { JPanel pSet = new JPanel(); pSet.setLayout(new GridBagLayout()); JButton setButton = new JButton(Bundle.getMessage("Set")); setButton.setName(rs.getId()); setButton.addActionListener( (ActionEvent e) -> { if (Car.class.isInstance(rs)) { carSetButtonActionPerfomed(e); } else { engineSetButtonActionPerfomed(e); } }); JLabel label = new JLabel( TrainCommon.padString( rs.toString(), Control.max_len_string_attibute + Control.max_len_string_road_number)); setLabelFont(label); addItem(pSet, label, 0, 0); addItemLeft(pSet, setButton, 1, 0); pSet.setAlignmentX(LEFT_ALIGNMENT); return pSet; } protected void setCheckBoxFont(JCheckBox checkBox) { if (Setup.isTabEnabled()) { Font font = new Font(Setup.getFontName(), Font.PLAIN, checkBox.getFont().getSize()); checkBox.setFont(font); } } protected void setLabelFont(JLabel label) { if (Setup.isTabEnabled()) { Font font = new Font(Setup.getFontName(), Font.PLAIN, label.getFont().getSize()); label.setFont(font); } } protected void setButtonText() { if (isSetMode) { setButton.setText(Bundle.getMessage("Done")); } else { setButton.setText(Bundle.getMessage("Set")); } } // returns one of two possible departure strings for a train protected String getStatus(RouteLocation rl, boolean isManifest) { if (rl == _train.getRoute().getTerminatesRouteLocation()) { return MessageFormat.format( TrainManifestText.getStringTrainTerminates(), new Object[] {_train.getTrainTerminatesName()}); } if (rl != _train.getCurrentLocation() && _train.getExpectedArrivalTime(rl).equals(Train.ALREADY_SERVICED)) { return MessageFormat.format( TrainSwitchListText.getStringTrainDone(), new Object[] {_train.getName()}); } if (!_train.isBuilt()) { return _train.getStatus(); } if (Setup.isPrintLoadsAndEmptiesEnabled()) { int emptyCars = _train.getNumberEmptyCarsInTrain(rl); String text; if (isManifest) { text = TrainManifestText.getStringTrainDepartsLoads(); } else { text = TrainSwitchListText.getStringTrainDepartsLoads(); } return MessageFormat.format( text, new Object[] { TrainCommon.splitString(rl.getName()), rl.getTrainDirectionString(), _train.getNumberCarsInTrain(rl) - emptyCars, emptyCars, _train.getTrainLength(rl), Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), _train.getTrainTerminatesName(), _train.getName() }); } else { String text; if (isManifest) { text = TrainManifestText.getStringTrainDepartsCars(); } else { text = TrainSwitchListText.getStringTrainDepartsCars(); } return MessageFormat.format( text, new Object[] { TrainCommon.splitString(rl.getName()), rl.getTrainDirectionString(), _train.getNumberCarsInTrain(rl), _train.getTrainLength(rl), Setup.getLengthUnit().toLowerCase(), _train.getTrainWeight(rl), _train.getTrainTerminatesName(), _train.getName() }); } } protected void clearAndUpdate() { trainCommon.clearUtilityCarTypes(); // reset the utility car counts checkBoxes.clear(); isSetMode = false; update(); } // to be overridden protected abstract void update(); protected void removePropertyChangeListerners() { rollingStock .stream() .forEach( (rs) -> { rs.removePropertyChangeListener(this); }); rollingStock.clear(); } @Override public void propertyChange(PropertyChangeEvent e) { log.debug( "Property change {} for: {} old: {} new: {}", e.getPropertyName(), e.getSource().toString(), e.getOldValue(), e.getNewValue()); // NOI18N } private static final Logger log = LoggerFactory.getLogger(CommonConductorYardmasterPanel.class.getName()); }
protected void moveRollingStock(Track fromTrack, Track toTrack) { moveRollingStock(fromTrack, toTrack, CarManager.instance()); moveRollingStock(fromTrack, toTrack, EngineManager.instance()); }