001package jmri.jmrit.operations.routes; 002 003import java.awt.Dimension; 004import java.awt.GridBagLayout; 005import java.text.MessageFormat; 006import java.util.List; 007 008import javax.swing.*; 009 010import org.slf4j.Logger; 011import org.slf4j.LoggerFactory; 012 013import jmri.InstanceManager; 014import jmri.jmrit.operations.OperationsFrame; 015import jmri.jmrit.operations.OperationsXml; 016import jmri.jmrit.operations.locations.Location; 017import jmri.jmrit.operations.locations.LocationManager; 018import jmri.jmrit.operations.routes.tools.PrintRouteAction; 019import jmri.jmrit.operations.routes.tools.RouteBlockingOrderEditFrameAction; 020import jmri.jmrit.operations.routes.tools.RouteCopyAction; 021import jmri.jmrit.operations.routes.tools.SetTrainIconRouteAction; 022import jmri.jmrit.operations.setup.Control; 023import jmri.jmrit.operations.setup.Setup; 024import jmri.jmrit.operations.trains.Train; 025import jmri.swing.JTablePersistenceManager; 026 027/** 028 * Frame for user edit of route 029 * 030 * @author Dan Boudreau Copyright (C) 2008, 2010, 2011, 2014, 2016 031 */ 032public class RouteEditFrame extends OperationsFrame implements java.beans.PropertyChangeListener { 033 034 RouteEditTableModel routeModel = new RouteEditTableModel(); 035 JTable routeTable = new JTable(routeModel); 036 JScrollPane routePane; 037 038 RouteManager routeManager; 039 040 Route _route = null; 041 Train _train = null; 042 043 // major buttons 044 JButton addLocationButton = new JButton(Bundle.getMessage("AddLocation")); 045 JButton saveRouteButton = new JButton(Bundle.getMessage("SaveRoute")); 046 JButton deleteRouteButton = new JButton(Bundle.getMessage("DeleteRoute")); 047 JButton addRouteButton = new JButton(Bundle.getMessage("AddRoute")); 048 049 // radio buttons 050 JRadioButton addLocAtTop = new JRadioButton(Bundle.getMessage("Top")); 051 JRadioButton addLocAtMiddle = new JRadioButton(Bundle.getMessage("Middle")); 052 JRadioButton addLocAtBottom = new JRadioButton(Bundle.getMessage("Bottom")); 053 054 JRadioButton showWait = new JRadioButton(Bundle.getMessage("Wait")); 055 JRadioButton showDepartTime = new JRadioButton(Bundle.getMessage("DepartTime")); 056 057 // text field 058 JTextField routeNameTextField = new JTextField(Control.max_len_string_route_name); 059 JTextField commentTextField = new JTextField(35); 060 061 // combo boxes 062 JComboBox<Location> locationBox = InstanceManager.getDefault(LocationManager.class).getComboBox(); 063 064 JMenu toolMenu = new JMenu(Bundle.getMessage("MenuTools")); 065 066 public static final String NAME = Bundle.getMessage("Name"); 067 public static final String DISPOSE = "dispose"; // NOI18N 068 069 public RouteEditFrame() { 070 super(Bundle.getMessage("TitleRouteEdit")); 071 } 072 073 public void initComponents(Route route, Train train) { 074 _train = train; // assign route to this train 075 initComponents(route); 076 } 077 078 public void initComponents(Route route) { 079 080 _route = route; 081 082 // load managers 083 routeManager = InstanceManager.getDefault(RouteManager.class); 084 085 // Set up the jtable in a Scroll Pane.. 086 routePane = new JScrollPane(routeTable); 087 routePane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED); 088 routePane.setBorder(BorderFactory.createTitledBorder("")); 089 090 routeModel.initTable(this, routeTable, _route); 091 092 if (_route != null) { 093 _route.addPropertyChangeListener(this); 094 routeNameTextField.setText(_route.getName()); 095 commentTextField.setText(_route.getComment()); 096 enableButtons(!route.getStatus().equals(Route.TRAIN_BUILT)); // do not allow user to modify a built train 097 addRouteButton.setEnabled(false); // override and disable 098 } else { 099 setTitle(Bundle.getMessage("TitleRouteAdd")); 100 enableButtons(false); 101 } 102 103 getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS)); 104 105 // Set up the panels 106 JPanel p1 = new JPanel(); 107 p1.setLayout(new BoxLayout(p1, BoxLayout.X_AXIS)); 108 JScrollPane p1Pane = new JScrollPane(p1); 109 p1Pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 110 p1Pane.setMinimumSize(new Dimension(300, 3 * routeNameTextField.getPreferredSize().height)); 111 p1Pane.setMaximumSize(new Dimension(2000, 200)); 112 p1Pane.setBorder(BorderFactory.createTitledBorder("")); 113 114 // name panel 115 JPanel pName = new JPanel(); 116 pName.setLayout(new GridBagLayout()); 117 pName.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Name"))); 118 addItem(pName, routeNameTextField, 0, 0); 119 120 // comment panel 121 JPanel pComment = new JPanel(); 122 pComment.setLayout(new GridBagLayout()); 123 pComment.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Comment"))); 124 addItem(pComment, commentTextField, 0, 0); 125 126 p1.add(pName); 127 p1.add(pComment); 128 129 JPanel p2 = new JPanel(); 130 p2.setLayout(new BoxLayout(p2, BoxLayout.X_AXIS)); 131 JScrollPane p2Pane = new JScrollPane(p2); 132 p2Pane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 133 p2Pane.setMinimumSize(new Dimension(300, 3 * routeNameTextField.getPreferredSize().height)); 134 p2Pane.setMaximumSize(new Dimension(2000, 200)); 135 p2Pane.setBorder(BorderFactory.createTitledBorder("")); 136 137 // location panel 138 JPanel pLoc = new JPanel(); 139 pLoc.setLayout(new GridBagLayout()); 140 pLoc.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Location"))); 141 addItem(pLoc, locationBox, 0, 1); 142 addItem(pLoc, addLocationButton, 1, 1); 143 addItem(pLoc, addLocAtTop, 2, 1); 144 addItem(pLoc, addLocAtMiddle, 3, 1); 145 addItem(pLoc, addLocAtBottom, 4, 1); 146 147 // Wait or Depart Time panel 148 JPanel pWait = new JPanel(); 149 pWait.setLayout(new GridBagLayout()); 150 pWait.setBorder(BorderFactory.createTitledBorder(Bundle.getMessage("Display"))); 151 addItem(pWait, showWait, 0, 1); 152 addItem(pWait, showDepartTime, 1, 1); 153 154 p2.add(pLoc); 155 p2.add(pWait); 156 157 // row 12 buttons 158 JPanel pB = new JPanel(); 159 pB.setLayout(new GridBagLayout()); 160 JScrollPane pBPane = new JScrollPane(pB); 161 pBPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_NEVER); 162 pBPane.setMinimumSize(new Dimension(300, 3 * routeNameTextField.getPreferredSize().height)); 163 pBPane.setMaximumSize(new Dimension(2000, 200)); 164 pBPane.setBorder(BorderFactory.createTitledBorder("")); 165 166 addItem(pB, deleteRouteButton, 0, 0); 167 addItem(pB, addRouteButton, 1, 0); 168 addItem(pB, saveRouteButton, 3, 0); 169 170 getContentPane().add(p1Pane); 171 getContentPane().add(routePane); 172 getContentPane().add(p2Pane); 173 getContentPane().add(pBPane); 174 175 // setup buttons 176 addButtonAction(addLocationButton); 177 addButtonAction(deleteRouteButton); 178 addButtonAction(addRouteButton); 179 addButtonAction(saveRouteButton); 180 181 // setup radio buttons 182 ButtonGroup group = new ButtonGroup(); 183 group.add(addLocAtTop); 184 group.add(addLocAtMiddle); 185 group.add(addLocAtBottom); 186 addLocAtBottom.setSelected(true); 187 188 addRadioButtonAction(addLocAtTop); // to clear table row sorting 189 addRadioButtonAction(addLocAtMiddle); 190 addRadioButtonAction(addLocAtBottom); // to clear table row sorting 191 192 ButtonGroup groupTime = new ButtonGroup(); 193 groupTime.add(showWait); 194 groupTime.add(showDepartTime); 195 addRadioButtonAction(showWait); 196 addRadioButtonAction(showDepartTime); 197 setTimeWaitRadioButtons(); 198 199 // build menu 200 JMenuBar menuBar = new JMenuBar(); 201 menuBar.add(toolMenu); 202 loadToolMenu(); 203 setJMenuBar(menuBar); 204 addHelpMenu("package.jmri.jmrit.operations.Operations_EditRoute", true); // NOI18N 205 206 // get notified if combo box gets modified 207 InstanceManager.getDefault(LocationManager.class).addPropertyChangeListener(this); 208 209 // set frame size and route for display 210 initMinimumSize(new Dimension(Control.panelWidth700, Control.panelHeight400)); 211 } 212 213 private void loadToolMenu() { 214 toolMenu.removeAll(); 215 toolMenu.add(new RouteBlockingOrderEditFrameAction(_route)); 216 toolMenu.add(new RouteCopyAction(_route)); 217 toolMenu.add(new SetTrainIconRouteAction(_route)); 218 toolMenu.addSeparator(); 219 toolMenu.add(new PrintRouteAction(false, _route)); 220 toolMenu.add(new PrintRouteAction(true, _route)); 221 } 222 223 // Save, Delete, Add 224 @Override 225 public void buttonActionPerformed(java.awt.event.ActionEvent ae) { 226 if (ae.getSource() == addLocationButton) { 227 log.debug("route add location button activated"); 228 if (locationBox.getSelectedItem() != null) { 229 addNewRouteLocation(); 230 } 231 } 232 if (ae.getSource() == saveRouteButton) { 233 log.debug("route save button activated"); 234 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 235 if (_route == null && route == null) { 236 saveNewRoute(); 237 } else { 238 if (route != null && route != _route) { 239 reportRouteExists(Bundle.getMessage("save")); 240 return; 241 } 242 saveRoute(); 243 } 244 if (Setup.isCloseWindowOnSaveEnabled()) { 245 dispose(); 246 } 247 } 248 if (ae.getSource() == deleteRouteButton) { 249 log.debug("route delete button activated"); 250 if (JOptionPane.showConfirmDialog(this, 251 MessageFormat.format(Bundle.getMessage("AreYouSure?"), 252 new Object[] { routeNameTextField.getText() }), 253 Bundle.getMessage("DeleteRoute?"), JOptionPane.YES_NO_OPTION) != JOptionPane.YES_OPTION) { 254 return; 255 } 256 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 257 if (route == null) { 258 return; 259 } 260 261 routeManager.deregister(route); 262 _route = null; 263 264 enableButtons(false); 265 routeModel.dispose(); 266 // save route file 267 OperationsXml.save(); 268 } 269 if (ae.getSource() == addRouteButton) { 270 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 271 if (route != null) { 272 reportRouteExists(Bundle.getMessage("add")); 273 return; 274 } 275 saveNewRoute(); 276 } 277 } 278 279 @Override 280 public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) { 281 routeModel.setWait(showWait.isSelected()); 282 } 283 284 private void addNewRouteLocation() { 285 if (routeTable.isEditing()) { 286 log.debug("route table edit true"); 287 routeTable.getCellEditor().stopCellEditing(); 288 } 289 // add location to this route 290 Location l = (Location) locationBox.getSelectedItem(); 291 RouteLocation rl; 292 if (addLocAtTop.isSelected()) { 293 // add location to start 294 rl = _route.addLocation(l, Route.START); 295 } else if (addLocAtMiddle.isSelected()) { 296 // add location to middle 297 if (routeTable.getSelectedRow() >= 0) { 298 int row = routeTable.getSelectedRow(); 299 rl = _route.addLocation(l, row + Route.START); 300 // we need to reselect the table since the content has changed 301 routeTable.getSelectionModel().setSelectionInterval(row + Route.START, row + Route.START); 302 } else { 303 rl = _route.addLocation(l, _route.size() / 2 + Route.START); 304 } 305 } else { 306 // add location to end 307 rl = _route.addLocation(l); 308 } 309 rl.setTrainDirection(routeModel.getLastTrainDirection()); 310 rl.setMaxTrainLength(routeModel.getLastMaxTrainLength()); 311 if (rl.getLocation().isStaging()) { 312 rl.setMaxCarMoves(50); 313 } else { 314 rl.setMaxCarMoves(routeModel.getLastMaxTrainMoves()); 315 } 316 // set train icon location 317 rl.setTrainIconCoordinates(); 318 } 319 320 private void saveNewRoute() { 321 if (!checkName(Bundle.getMessage("add"))) { 322 return; 323 } 324 Route route = routeManager.newRoute(routeNameTextField.getText()); 325 routeModel.initTable(this, routeTable, route); 326 _route = route; 327 enableButtons(true); 328 // assign route to a train? 329 if (_train != null) { 330 _train.setRoute(route); 331 } 332 if (_route != null) { 333 _route.addPropertyChangeListener(this); 334 } 335 saveRoute(); 336 loadToolMenu(); 337 } 338 339 private void saveRoute() { 340 if (!checkName(Bundle.getMessage("save"))) { 341 return; 342 } 343 _route.setName(routeNameTextField.getText()); 344 _route.setComment(commentTextField.getText()); 345 346 if (routeTable.isEditing()) { 347 log.debug("route table edit true"); 348 routeTable.getCellEditor().stopCellEditing(); 349 } 350 checkTrainDirections(); 351 // save route file 352 OperationsXml.save(); 353 } 354 355 /** 356 * 357 * @return true if name is length is okay 358 */ 359 private boolean checkName(String s) { 360 if (routeNameTextField.getText().trim().isEmpty()) { 361 log.debug("Must enter a name for the route"); 362 JOptionPane.showMessageDialog(this, Bundle.getMessage("MustEnterName"), 363 MessageFormat.format(Bundle.getMessage("CanNotRoute"), new Object[] { s }), 364 JOptionPane.ERROR_MESSAGE); 365 return false; 366 } 367 if (routeNameTextField.getText().length() > Control.max_len_string_route_name) { 368 JOptionPane.showMessageDialog(this, 369 MessageFormat.format(Bundle.getMessage("RouteNameLess"), 370 new Object[] { Control.max_len_string_route_name + 1 }), 371 MessageFormat.format(Bundle.getMessage("CanNotRoute"), new Object[] { s }), 372 JOptionPane.ERROR_MESSAGE); 373 return false; 374 } 375 return true; 376 } 377 378 /* 379 * Checks to see if user has disabled the saved train directions for this route. 380 */ 381 private void checkTrainDirections() { 382 // get the valid train directions 383 List<String> directions = Setup.getTrainDirectionList(); 384 for (RouteLocation rl : _route.getLocationsBySequenceList()) { 385 if (!directions.contains(rl.getTrainDirectionString())) { 386 JOptionPane.showMessageDialog(this, 387 MessageFormat.format(Bundle.getMessage("RouteDirection"), new Object[] { rl.getId() }), 388 MessageFormat.format(Bundle.getMessage("RouteDirectionError"), 389 new Object[] { rl.getTrainDirectionString() }), 390 JOptionPane.ERROR_MESSAGE); 391 return; 392 } 393 } 394 } 395 396 private void reportRouteExists(String s) { 397 log.info("Can not {}, route already exists", s); 398 JOptionPane.showMessageDialog(this, Bundle.getMessage("ReportExists"), 399 MessageFormat.format(Bundle.getMessage("CanNotRoute"), new Object[] { s }), JOptionPane.ERROR_MESSAGE); 400 } 401 402 private void enableButtons(boolean enabled) { 403 toolMenu.setEnabled(enabled); 404 locationBox.setEnabled(enabled); 405 addLocationButton.setEnabled(enabled); 406 addLocAtTop.setEnabled(enabled); 407 addLocAtMiddle.setEnabled(enabled); 408 addLocAtBottom.setEnabled(enabled); 409 saveRouteButton.setEnabled(enabled); 410 deleteRouteButton.setEnabled(enabled); 411 routeTable.setEnabled(enabled); 412 showWait.setEnabled(enabled); 413 showDepartTime.setEnabled(enabled); 414 // the inverse! 415 addRouteButton.setEnabled(!enabled); 416 } 417 418 @Override 419 public void dispose() { 420 InstanceManager.getOptionalDefault(JTablePersistenceManager.class).ifPresent(tpm -> { 421 tpm.stopPersisting(routeTable); 422 }); 423 if (_route != null) { 424 _route.removePropertyChangeListener(this); 425 } 426 routeModel.dispose(); 427 super.dispose(); 428 } 429 430 private void updateComboBoxes() { 431 InstanceManager.getDefault(LocationManager.class).updateComboBox(locationBox); 432 } 433 434 // if the route has a departure time in the first location set the 435 // showDepartTime radio button 436 private void setTimeWaitRadioButtons() { 437 showWait.setSelected(true); 438 if (_route != null) { 439 RouteLocation rl = _route.getDepartsRouteLocation(); 440 if (rl != null && !rl.getDepartureTime().equals(RouteLocation.NONE)) { 441 showDepartTime.setSelected(true); 442 } 443 routeModel.setWait(showWait.isSelected()); 444 } 445 } 446 447 @Override 448 public void propertyChange(java.beans.PropertyChangeEvent e) { 449 if (Control.SHOW_PROPERTY) { 450 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 451 e.getNewValue()); 452 } 453 if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) { 454 updateComboBoxes(); 455 } 456 if (e.getPropertyName().equals(Route.ROUTE_STATUS_CHANGED_PROPERTY)) { 457 enableButtons(!_route.getStatus().equals(Route.TRAIN_BUILT)); // do not allow user to modify a built train 458 addRouteButton.setEnabled(false); // override and disable 459 } 460 } 461 462 private final static Logger log = LoggerFactory.getLogger(RouteEditFrame.class); 463}