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