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