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 showWait = new JRadioButton(Bundle.getMessage("Wait")); 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, showWait, 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(showWait); 188 groupTime.add(showDepartTime); 189 addRadioButtonAction(showWait); 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_EditRoute", 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 } 225 } 226 if (ae.getSource() == saveRouteButton) { 227 log.debug("route save button activated"); 228 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 229 if (_route == null && route == null) { 230 saveNewRoute(); // can't happen, save button is disabled 231 } else { 232 if (route != null && route != _route) { 233 reportRouteExists(Bundle.getMessage("save")); 234 return; 235 } 236 if (saveRoute() && Setup.isCloseWindowOnSaveEnabled()) { 237 dispose(); 238 } 239 } 240 } 241 if (ae.getSource() == deleteRouteButton) { 242 log.debug("route delete button activated"); 243 if (JmriJOptionPane.showConfirmDialog(this, 244 Bundle.getMessage("AreYouSure?", 245 routeNameTextField.getText()), 246 Bundle.getMessage("DeleteRoute?"), JmriJOptionPane.YES_NO_OPTION) != JmriJOptionPane.YES_OPTION) { 247 return; 248 } 249 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 250 if (route == null) { 251 return; 252 } 253 254 routeManager.deregister(route); 255 _route = null; 256 257 enableButtons(false); 258 routeModel.dispose(); 259 // save route file 260 OperationsXml.save(); 261 } 262 if (ae.getSource() == addRouteButton) { 263 Route route = routeManager.getRouteByName(routeNameTextField.getText()); 264 if (route != null) { 265 reportRouteExists(Bundle.getMessage("add")); 266 return; 267 } 268 saveNewRoute(); 269 } 270 } 271 272 @Override 273 public void radioButtonActionPerformed(java.awt.event.ActionEvent ae) { 274 routeModel.setWait(showWait.isSelected()); 275 } 276 277 private void addNewRouteLocation() { 278 if (routeTable.isEditing()) { 279 log.debug("route table edit true"); 280 routeTable.getCellEditor().stopCellEditing(); 281 } 282 // add location to this route 283 Location l = (Location) locationBox.getSelectedItem(); 284 RouteLocation rl; 285 if (addLocAtTop.isSelected()) { 286 // add location to start 287 rl = _route.addLocation(l, Route.START); 288 } else if (addLocAtMiddle.isSelected()) { 289 // add location to middle 290 if (routeTable.getSelectedRow() >= 0) { 291 int row = routeTable.getSelectedRow(); 292 rl = _route.addLocation(l, row + Route.START); 293 // we need to reselect the table since the content has changed 294 routeTable.getSelectionModel().setSelectionInterval(row + Route.START, row + Route.START); 295 } else { 296 rl = _route.addLocation(l, _route.size() / 2 + Route.START); 297 } 298 } else { 299 // add location to end 300 rl = _route.addLocation(l); 301 } 302 rl.setTrainDirection(routeModel.getLastTrainDirection()); 303 rl.setMaxTrainLength(routeModel.getLastMaxTrainLength()); 304 if (rl.getLocation().isStaging()) { 305 rl.setMaxCarMoves(50); 306 } else { 307 rl.setMaxCarMoves(routeModel.getLastMaxTrainMoves()); 308 } 309 // set train icon location 310 rl.setTrainIconCoordinates(); 311 } 312 313 private void saveNewRoute() { 314 if (!checkName(Bundle.getMessage("add"))) { 315 return; 316 } 317 Route route = routeManager.newRoute(routeNameTextField.getText()); 318 routeModel.initTable(this, routeTable, route); 319 _route = route; 320 enableButtons(true); 321 // assign route to a train? 322 if (_train != null) { 323 _train.setRoute(route); 324 } 325 if (_route != null) { 326 _route.addPropertyChangeListener(this); 327 } 328 saveRoute(); 329 loadToolMenu(); 330 } 331 332 private boolean saveRoute() { 333 if (routeTable.isEditing()) { 334 log.debug("route table edit true"); 335 routeTable.getCellEditor().stopCellEditing(); 336 } 337 338 if (!checkName(Bundle.getMessage("save")) || !checkTrainDirections()) { 339 return false; 340 } 341 _route.setName(routeNameTextField.getText()); 342 _route.setComment(commentTextField.getText()); 343 344 // save route file 345 OperationsXml.save(); 346 return true; 347 } 348 349 /** 350 * 351 * @return true if name is length is okay 352 */ 353 private boolean checkName(String s) { 354 if (routeNameTextField.getText().trim().isEmpty()) { 355 log.debug("Must enter a name for the route"); 356 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("MustEnterName"), 357 Bundle.getMessage("CanNotRoute", s), 358 JmriJOptionPane.ERROR_MESSAGE); 359 return false; 360 } 361 if (routeNameTextField.getText().length() > Control.max_len_string_route_name) { 362 JmriJOptionPane.showMessageDialog(this, 363 Bundle.getMessage("RouteNameLess", 364 Control.max_len_string_route_name + 1), 365 Bundle.getMessage("CanNotRoute", s), 366 JmriJOptionPane.ERROR_MESSAGE); 367 return false; 368 } 369 return true; 370 } 371 372 /* 373 * Checks to see if user has disabled the saved train directions for this route. 374 */ 375 private boolean checkTrainDirections() { 376 // get the valid train directions 377 List<String> directions = Setup.getTrainDirectionList(); 378 for (RouteLocation rl : _route.getLocationsBySequenceList()) { 379 if (!directions.contains(rl.getTrainDirectionString())) { 380 JmriJOptionPane.showMessageDialog(this, 381 Bundle.getMessage("RouteDirection", rl.getId()), 382 Bundle.getMessage("RouteDirectionError", 383 rl.getTrainDirectionString()), 384 JmriJOptionPane.ERROR_MESSAGE); 385 return false; 386 } 387 } 388 return true; 389 } 390 391 private void reportRouteExists(String s) { 392 JmriJOptionPane.showMessageDialog(this, Bundle.getMessage("ReportExists"), 393 Bundle.getMessage("CanNotRoute", s), JmriJOptionPane.ERROR_MESSAGE); 394 } 395 396 private void enableButtons(boolean enabled) { 397 toolMenu.setEnabled(enabled); 398 locationBox.setEnabled(enabled); 399 addLocationButton.setEnabled(enabled); 400 addLocAtTop.setEnabled(enabled); 401 addLocAtMiddle.setEnabled(enabled); 402 addLocAtBottom.setEnabled(enabled); 403 saveRouteButton.setEnabled(enabled); 404 deleteRouteButton.setEnabled(enabled); 405 routeTable.setEnabled(enabled); 406 showWait.setEnabled(enabled); 407 showDepartTime.setEnabled(enabled); 408 // the inverse! 409 addRouteButton.setEnabled(!enabled); 410 } 411 412 @Override 413 public void dispose() { 414 InstanceManager.getOptionalDefault(JTablePersistenceManager.class).ifPresent(tpm -> { 415 tpm.stopPersisting(routeTable); 416 }); 417 if (_route != null) { 418 _route.removePropertyChangeListener(this); 419 } 420 routeModel.dispose(); 421 super.dispose(); 422 } 423 424 private void updateComboBoxes() { 425 InstanceManager.getDefault(LocationManager.class).updateComboBox(locationBox); 426 } 427 428 // if the route has a departure time in the first location set the 429 // showDepartTime radio button 430 private void setTimeWaitRadioButtons() { 431 showWait.setSelected(true); 432 if (_route != null) { 433 RouteLocation rl = _route.getDepartsRouteLocation(); 434 if (rl != null && !rl.getDepartureTime().equals(RouteLocation.NONE)) { 435 showDepartTime.setSelected(true); 436 } 437 routeModel.setWait(showWait.isSelected()); 438 } 439 } 440 441 @Override 442 public void propertyChange(java.beans.PropertyChangeEvent e) { 443 if (Control.SHOW_PROPERTY) { 444 log.debug("Property change: ({}) old: ({}) new: ({})", e.getPropertyName(), e.getOldValue(), 445 e.getNewValue()); 446 } 447 if (e.getPropertyName().equals(LocationManager.LISTLENGTH_CHANGED_PROPERTY)) { 448 updateComboBoxes(); 449 } 450 if (e.getPropertyName().equals(Route.ROUTE_STATUS_CHANGED_PROPERTY)) { 451 enableButtons(!_route.getStatus().equals(Route.TRAIN_BUILT)); // do not allow user to modify a built train 452 addRouteButton.setEnabled(false); // override and disable 453 } 454 } 455 456 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(RouteEditFrame.class); 457}