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