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