001package jmri.jmrit.throttle; 002 003import java.awt.*; 004import java.awt.event.*; 005import java.beans.PropertyChangeListener; 006import java.beans.PropertyVetoException; 007import java.io.File; 008import java.net.URI; 009import java.util.*; 010import java.util.List; 011 012import javax.annotation.CheckForNull; 013import javax.swing.*; 014 015import jmri.*; 016import jmri.jmrit.catalog.NamedIcon; 017import jmri.jmrit.jython.Jynstrument; 018import jmri.jmrit.jython.JynstrumentFactory; 019import jmri.util.FileUtil; 020import jmri.util.JmriJFrame; 021import jmri.util.iharder.dnd.URIDrop; 022import jmri.util.swing.TransparencyUtils; 023 024import org.jdom2.Element; 025import org.jdom2.Attribute; 026 027import org.slf4j.Logger; 028import org.slf4j.LoggerFactory; 029 030// Should be named ThrottleFrame, but ThrottleFrame already exit, hence ThrottleWindow 031public class ThrottleWindow extends JmriJFrame implements ThrottleControllersUIContainer { 032 033 private final jmri.jmrix.ConnectionConfig connectionConfig; 034 private final ThrottleManager throttleManager; 035 private final ThrottleFrameManager throttleFrameManager = InstanceManager.getDefault(ThrottleFrameManager.class); 036 037 private JPanel throttlesPanel; 038 private ThrottleFrame currentThrottleFrame; 039 private CardLayout throttlesLayout; 040 041 private JCheckBoxMenuItem viewControlPanel; 042 private JCheckBoxMenuItem viewFunctionPanel; 043 private JCheckBoxMenuItem viewAddressPanel; 044 private JCheckBoxMenuItem viewSpeedPanel; 045 private JMenuItem viewAllButtons; 046 private JMenuItem fileMenuSave; 047 private JMenuItem editMenuExportRoster; 048 049 private JButton jbPrevious = null; 050 private JButton jbNext = null; 051 private JButton jbPreviousRunning = null; 052 private JButton jbNextRunning = null; 053 private JButton jbThrottleList = null; 054 private JButton jbNew = null; 055 private JButton jbClose = null; 056 private JButton jbMode = null; 057 private JToolBar throttleToolBar; 058 059 private String titleText = ""; 060 private String titleTextType = "rosterID"; 061 private boolean isEditMode = true; 062 063 private final PowerManager powerMgr; 064 private SmallPowerManagerButton smallPowerMgmtButton; 065 066 private final ThrottleWindowActionsFactory myActionFactory; 067 068 private ArrayList<ThrottleFrame> throttleFrames = new ArrayList<>(5); 069 070 java.beans.PropertyChangeSupport pcs = new java.beans.PropertyChangeSupport(this); 071 072 /** 073 * Default constructor 074 */ 075 public ThrottleWindow() { 076 this((jmri.jmrix.ConnectionConfig) null); 077 } 078 079 /** 080 * Constructor 081 * @param connectionConfig the connection config 082 */ 083 public ThrottleWindow(jmri.jmrix.ConnectionConfig connectionConfig) { 084 super(Bundle.getMessage("ThrottleTitle")); 085 this.connectionConfig = connectionConfig; 086 if (connectionConfig != null) { 087 this.throttleManager = connectionConfig.getAdapter().getSystemConnectionMemo().get(jmri.ThrottleManager.class); 088 } else { 089 this.throttleManager = InstanceManager.getDefault(jmri.ThrottleManager.class); 090 } 091 092 myActionFactory = new ThrottleWindowActionsFactory(this); 093 powerMgr = InstanceManager.getNullableDefault(PowerManager.class); 094 if (powerMgr == null) { 095 log.info("No power manager instance found, panel not active"); 096 } 097 pcs.addPropertyChangeListener(throttleFrameManager.getThrottlesListPanel().getTableModel()); 098 initGUI(); 099 applyPreferences(); 100 } 101 102 /** 103 * Create a ThrottleWindow 104 * @param e the xml element for the throttle window 105 * @return the throttle window 106 */ 107 public static ThrottleWindow createThrottleWindow(Element e) { 108 jmri.jmrix.ConnectionConfig connectionConfig = null; 109 110 Attribute systemPrefixAttr = e.getAttribute("systemPrefix"); 111 if (systemPrefixAttr != null) { 112 String systemPrefix = systemPrefixAttr.getValue(); 113 // Set connectionConfig to null in case the systemPrefix 114 // points to a connection that doesn't exist anymore. 115 116 for (jmri.jmrix.ConnectionConfig c : InstanceManager.getDefault(jmri.jmrix.ConnectionConfigManager.class)) { 117 if (c.getAdapter().getSystemPrefix().equals(systemPrefix)) { 118 connectionConfig = c; 119 } 120 } 121 } 122 123 ThrottleWindow tw = new ThrottleWindow(connectionConfig); 124 tw.setXml(e); 125 return tw; 126 } 127 128 private void initGUI() { 129 setTitle(Bundle.getMessage("ThrottleTitle")); 130 setLayout(new BorderLayout()); 131 throttlesLayout = new CardLayout(); 132 throttlesPanel = new JPanel(throttlesLayout); 133 throttlesPanel.setDoubleBuffered(true); 134 135 initializeToolbar(); 136 initializeMenu(); 137 138 String txt = "ThrottleFrame-" + throttleFrameManager.generateUniqueFrameID(); 139 setCurrentThrottleFrame(new ThrottleFrame(this, throttleManager)); 140 getCurrentThrottleFrame().setTitle(txt); 141 throttlesPanel.add(txt, getCurrentThrottleFrame()); 142 throttleFrames.add(getCurrentThrottleFrame()); 143 add(throttlesPanel, BorderLayout.CENTER); 144 145 installInputsListenerOnAllComponents(this); 146 // to get something to put focus on 147 getRootPane().setFocusable(true); 148 149 ActionMap am = myActionFactory.buildActionMap(); 150 for (Object k : am.allKeys()) { 151 getRootPane().getActionMap().put(k, am.get(k)); 152 } 153 154 addMouseWheelListener( new ThrottleWindowInputsListener(this) ); 155 156 this.addWindowListener(new WindowAdapter() { 157 @Override 158 public void windowClosing(WindowEvent e) { 159 ThrottleWindow me = (ThrottleWindow) e.getSource(); 160 throttleFrameManager.requestThrottleWindowDestruction(me); 161 if (throttleToolBar != null) { 162 Component[] cmps = throttleToolBar.getComponents(); 163 if (cmps != null) { 164 for (Component cmp : cmps) { 165 if (cmp instanceof Jynstrument) { 166 ((Jynstrument) cmp).exit(); 167 } 168 } 169 } 170 } 171 } 172 173 @Override 174 public void windowOpened(WindowEvent e) { 175 try { // on initial open, force selection of address panel 176 getCurrentThrottleFrame().getAddressPanel().setSelected(true); 177 } catch (PropertyVetoException ex) { 178 log.warn("Unable to force selection of address panel", ex); 179 } 180 } 181 }); 182 updateGUI(); 183 } 184 185 public void updateGUI() { 186 if (getCurrentThrottleFrame() == null) { 187 return; 188 } 189 // title bar 190 getCurrentThrottleFrame().setFrameTitle(); 191 // menu items 192 viewAddressPanel.setEnabled(isEditMode); 193 viewControlPanel.setEnabled(isEditMode); 194 viewFunctionPanel.setEnabled(isEditMode); 195 viewSpeedPanel.setEnabled(isEditMode); 196 if (isEditMode) { 197 viewAddressPanel.setSelected(getCurrentThrottleFrame().getAddressPanel().isVisible()); 198 viewControlPanel.setSelected(getCurrentThrottleFrame().getControlPanel().isVisible()); 199 viewFunctionPanel.setSelected(getCurrentThrottleFrame().getFunctionPanel().isVisible()); 200 viewSpeedPanel.setSelected(getCurrentThrottleFrame().getSpeedPanel().isVisible()); 201 } 202 fileMenuSave.setEnabled(getCurrentThrottleFrame().getLastUsedSaveFile() != null || getCurrentThrottleFrame().getRosterEntry() != null); 203 editMenuExportRoster.setEnabled(getCurrentThrottleFrame().getRosterEntry() != null); 204 // toolbar items 205 if (jbPrevious != null) // means toolbar enabled 206 { 207 if (throttleFrames.size() > 1) { 208 jbPrevious.setEnabled(true); 209 jbNext.setEnabled(true); 210 jbClose.setEnabled(true); 211 jbPreviousRunning.setEnabled(true); 212 jbNextRunning.setEnabled(true); 213 } else { 214 jbPrevious.setEnabled(false); 215 jbNext.setEnabled(false); 216 jbClose.setEnabled(false); 217 jbPreviousRunning.setEnabled(false); 218 jbNextRunning.setEnabled(false); 219 } 220 } 221 getRootPane().requestFocusInWindow(); 222 } 223 224 private void initializeToolbar() { 225 throttleToolBar = new JToolBar("Throttles toolbar"); 226 227 jbNew = new JButton(); 228 // nouveau.setText(Bundle.getMessage("ThrottleToolBarNew")); 229 jbNew.setIcon(new NamedIcon("resources/icons/throttles/add.png", "resources/icons/throttles/add.png")); 230 jbNew.setToolTipText(Bundle.getMessage("ThrottleToolBarNewToolTip")); 231 jbNew.setVerticalTextPosition(JButton.BOTTOM); 232 jbNew.setHorizontalTextPosition(JButton.CENTER); 233 jbNew.addActionListener(e -> newThrottleController()); 234 throttleToolBar.add(jbNew); 235 236 jbClose = new JButton(); 237// close.setText(Bundle.getMessage("ThrottleToolBarClose")); 238 jbClose.setIcon(new NamedIcon("resources/icons/throttles/remove.png", "resources/icons/throttles/remove.png")); 239 jbClose.setToolTipText(Bundle.getMessage("ThrottleToolBarCloseToolTip")); 240 jbClose.setVerticalTextPosition(JButton.BOTTOM); 241 jbClose.setHorizontalTextPosition(JButton.CENTER); 242 jbClose.addActionListener(e -> removeAndDisposeCurentThrottleFrame()); 243 throttleToolBar.add(jbClose); 244 245 throttleToolBar.addSeparator(); 246 247 jbPreviousRunning = new JButton(); 248 jbPreviousRunning.setIcon(new NamedIcon("resources/icons/throttles/previous-jump.png", "resources/icons/throttles/previous-jump.png")); 249 jbPreviousRunning.setVerticalTextPosition(JButton.BOTTOM); 250 jbPreviousRunning.setHorizontalTextPosition(JButton.CENTER); 251 jbPreviousRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevRunToolTip")); 252 jbPreviousRunning.addActionListener(e -> previousRunningThrottleFrame()); 253 throttleToolBar.add(jbPreviousRunning); 254 255 jbPrevious = new JButton(); 256 jbPrevious.setIcon(new NamedIcon("resources/icons/throttles/previous.png", "resources/icons/throttles/previous.png")); 257 jbPrevious.setVerticalTextPosition(JButton.BOTTOM); 258 jbPrevious.setHorizontalTextPosition(JButton.CENTER); 259 jbPrevious.setToolTipText(Bundle.getMessage("ThrottleToolBarPrevToolTip")); 260 jbPrevious.addActionListener(e -> previousThrottleFrame()); 261 throttleToolBar.add(jbPrevious); 262 263 jbNext = new JButton(); 264 // next.setText(Bundle.getMessage("ThrottleToolBarNext")); 265 jbNext.setIcon(new NamedIcon("resources/icons/throttles/next.png", "resources/icons/throttles/next.png")); 266 jbNext.setToolTipText(Bundle.getMessage("ThrottleToolBarNextToolTip")); 267 jbNext.setVerticalTextPosition(JButton.BOTTOM); 268 jbNext.setHorizontalTextPosition(JButton.CENTER); 269 jbNext.addActionListener(e -> nextThrottleFrame()); 270 throttleToolBar.add(jbNext); 271 272 jbNextRunning = new JButton(); 273 jbNextRunning.setIcon(new NamedIcon("resources/icons/throttles/next-jump.png", "resources/icons/throttles/next-jump.png")); 274 jbNextRunning.setToolTipText(Bundle.getMessage("ThrottleToolBarNextRunToolTip")); 275 jbNextRunning.setVerticalTextPosition(JButton.BOTTOM); 276 jbNextRunning.setHorizontalTextPosition(JButton.CENTER); 277 jbNextRunning.addActionListener(e -> nextRunningThrottleFrame()); 278 throttleToolBar.add(jbNextRunning); 279 280 throttleToolBar.addSeparator(); 281 282 throttleToolBar.add(new StopAllButton()); 283 284 if (powerMgr != null) { 285 throttleToolBar.add(new LargePowerManagerButton(false)); 286 } 287 288 throttleToolBar.addSeparator(); 289 290 jbMode = new JButton(); 291 jbMode.setIcon(new NamedIcon("resources/icons/throttles/edit-view.png", "resources/icons/throttles/edit-view.png")); 292 jbMode.setToolTipText(Bundle.getMessage("ThrottleToolBarEditToolTip")); 293 jbMode.setVerticalTextPosition(JButton.BOTTOM); 294 jbMode.setHorizontalTextPosition(JButton.CENTER); 295 jbMode.addActionListener(e -> setEditMode( !isEditMode )); 296 throttleToolBar.add(jbMode); 297 298 throttleToolBar.addSeparator(); 299 300 jbThrottleList = new JButton(); 301 jbThrottleList.setIcon(new NamedIcon("resources/icons/throttles/list.png", "resources/icons/throttles/list.png")); 302 jbThrottleList.setToolTipText(Bundle.getMessage("ThrottleToolBarOpenThrottleListToolTip")); 303 jbThrottleList.setVerticalTextPosition(JButton.BOTTOM); 304 jbThrottleList.setHorizontalTextPosition(JButton.CENTER); 305 jbThrottleList.addActionListener(new ThrottlesListAction()); 306 throttleToolBar.add(jbThrottleList); 307 308 // Receptacle for Jynstruments 309 new URIDrop(throttleToolBar, uris -> { 310 for (URI uri : uris ) { 311 ynstrument(new File(uri).getPath()); 312 } 313 }); 314 315 add(throttleToolBar, BorderLayout.PAGE_START); 316 } 317 318 /** 319 * Return the number of active thottle frames. 320 * 321 * @return the number of active thottle frames. 322 */ 323 @Override 324 public int getNbThrottlesControllers() { 325 return throttleFrames.size() ; 326 } 327 328 /** 329 * Return the nth thottle frame of that throttle window 330 * 331 * @param n index of thr throtle frame 332 * @return the nth thottle frame of that throttle window 333 */ 334 @Override 335 public ThrottleControllerUI getThrottleControllerAt(int n) { 336 if (! (n < throttleFrames.size())) { 337 return null; 338 } 339 return throttleFrames.get(n); 340 } 341 342 /** 343 * Get the number of usages of a particular Loco Address. 344 * @param la the Loco Address, can be null. 345 * @return 0 if no usages, else number of AddressPanel usages. 346 */ 347 @Override 348 public int getNumberOfEntriesFor(@CheckForNull DccLocoAddress la) { 349 if (la == null) { 350 return 0; 351 } 352 int ret = 0; 353 for (ThrottleFrame tf: throttleFrames) { 354 AddressPanel ap = tf.getAddressPanel(); 355 if ( ap.getThrottle() != null && 356 ( la.equals( ap.getCurrentAddress()) || la.equals(ap.getConsistAddress()) ) ) { 357 ret++; // in use, increment count. 358 } 359 } 360 return ret; 361 } 362 363 @Override 364 public void emergencyStopAll() { 365 if (!throttleFrames.isEmpty()) { 366 for (ThrottleFrame tf: throttleFrames) { 367 tf.eStop(); 368 } 369 } 370 } 371 372 /** {@inheritDoc} */ 373 @Override 374 public void setTitle(String title) { 375 if (connectionConfig != null) { 376 super.setTitle(Bundle.getMessage("ThrottleTitleWithConnection", title, connectionConfig.getConnectionName())); 377 } else { 378 super.setTitle(title); 379 } 380 } 381 382 public void setEditMode(boolean mode) { 383 if (mode == isEditMode) 384 return; 385 isEditMode = mode; 386 if (!throttleFrames.isEmpty()) { 387 for (ThrottleFrame tf: throttleFrames) { 388 tf.setEditMode(isEditMode); 389 } 390 } 391 updateGUI(); 392 } 393 394 public boolean isEditMode() { 395 return isEditMode; 396 } 397 398 public Jynstrument ynstrument(String path) { 399 Jynstrument it = JynstrumentFactory.createInstrument(path, this); 400 if (it == null) { 401 log.error("Error while creating Jynstrument {}", path); 402 return null; 403 } 404 TransparencyUtils.setTransparent(it, true); 405 it.setVisible(true); 406 throttleToolBar.add(it); 407 throttleToolBar.repaint(); 408 return it; 409 } 410 411 /** 412 * Set up View, Edit and Power Menus 413 */ 414 private void initializeMenu() { 415 JMenu fileMenu = new JMenu(Bundle.getMessage("MenuFile")); 416 417 JMenuItem fileMenuLoad = new JMenuItem(Bundle.getMessage("ThrottleFileMenuLoadThrottle")); 418 fileMenuLoad.addActionListener(new AbstractAction() { 419 420 @Override 421 public void actionPerformed(ActionEvent e) { 422 getCurrentThrottleFrame().loadThrottle(); 423 } 424 }); 425 fileMenuSave = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveThrottle")); 426 fileMenuSave.addActionListener(new AbstractAction() { 427 428 @Override 429 public void actionPerformed(ActionEvent e) { 430 getCurrentThrottleFrame().saveThrottle(); 431 } 432 }); 433 JMenuItem fileMenuSaveAs = new JMenuItem(Bundle.getMessage("ThrottleFileMenuSaveAsThrottle")); 434 fileMenuSaveAs.addActionListener(new AbstractAction() { 435 436 @Override 437 public void actionPerformed(ActionEvent e) { 438 getCurrentThrottleFrame().saveThrottleAs(); 439 } 440 }); 441 442 jmri.jmrit.throttle.ThrottleCreationAction.addNewThrottleItemsToThrottleMenu(fileMenu); 443 fileMenu.add(fileMenuLoad); 444 fileMenu.add(fileMenuSave); 445 fileMenu.add(fileMenuSaveAs); 446 fileMenu.addSeparator(); 447 448 fileMenu.add(new jmri.jmrit.throttle.LoadXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadThrottleLayout"))); 449 fileMenu.add(new jmri.jmrit.throttle.StoreXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveThrottleLayout"))); 450 fileMenu.addSeparator(); 451 fileMenu.add(new jmri.jmrit.throttle.LoadDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemLoadDefaultThrottleLayout"))); 452 fileMenu.add(new jmri.jmrit.throttle.StoreDefaultXmlThrottlesLayoutAction(Bundle.getMessage("MenuItemSaveAsDefaultThrottleLayout"))); 453 fileMenu.addSeparator(); 454 fileMenu.add(new jmri.jmrit.withrottle.WiThrottleCreationAction(Bundle.getMessage("MenuItemStartWiThrottle"))); 455 456 JMenu viewMenu = new JMenu(Bundle.getMessage("ThrottleMenuView")); 457 viewAddressPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewAddressPanel")); 458 viewAddressPanel.setSelected(true); 459 viewAddressPanel.addItemListener(e -> getCurrentThrottleFrame().getAddressPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED)); 460 461 viewControlPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewControlPanel")); 462 viewControlPanel.setSelected(true); 463 viewControlPanel.addItemListener(e -> getCurrentThrottleFrame().getControlPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED)); 464 viewFunctionPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewFunctionPanel")); 465 viewFunctionPanel.setSelected(true); 466 viewFunctionPanel.addItemListener(e -> getCurrentThrottleFrame().getFunctionPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED)); 467 viewSpeedPanel = new JCheckBoxMenuItem(Bundle.getMessage("ThrottleMenuViewSpeedPanel")); 468 viewSpeedPanel.setSelected(false); 469 viewSpeedPanel.addItemListener(e -> getCurrentThrottleFrame().getSpeedPanel().setVisible(e.getStateChange() == ItemEvent.SELECTED)); 470 471 viewAllButtons = new JMenuItem(Bundle.getMessage("ThrottleMenuViewAllFunctionButtons")); 472 viewAllButtons.addActionListener(new AbstractAction() { 473 474 @Override 475 public void actionPerformed(ActionEvent ev) { 476 getCurrentThrottleFrame().getFunctionPanel().resetFnButtons(); 477 getCurrentThrottleFrame().getFunctionPanel().setEnabled(); 478 } 479 }); 480 481 JMenuItem makeAllComponentsInBounds = new JMenuItem(Bundle.getMessage("ThrottleMenuViewMakeAllComponentsInBounds")); 482 makeAllComponentsInBounds.addActionListener(new AbstractAction() { 483 484 @Override 485 public void actionPerformed(ActionEvent ev) { 486 getCurrentThrottleFrame().makeAllComponentsInBounds(); 487 } 488 }); 489 490 JMenuItem switchViewMode = new JMenuItem(Bundle.getMessage("ThrottleMenuViewSwitchMode")); 491 switchViewMode.addActionListener(new AbstractAction() { 492 493 @Override 494 public void actionPerformed(ActionEvent ev) { 495 setEditMode(!isEditMode); 496 } 497 }); 498 JMenuItem viewThrottlesList = new JMenuItem(Bundle.getMessage("ThrottleMenuViewViewThrottleList")); 499 viewThrottlesList.addActionListener(new ThrottlesListAction()); 500 501 viewMenu.add(viewAddressPanel); 502 viewMenu.add(viewControlPanel); 503 viewMenu.add(viewFunctionPanel); 504 viewMenu.add(viewSpeedPanel); 505 viewMenu.addSeparator(); 506 viewMenu.add(viewAllButtons); 507 viewMenu.add(makeAllComponentsInBounds); 508 viewMenu.addSeparator(); 509 viewMenu.add(switchViewMode); 510 viewMenu.add(viewThrottlesList); 511 512 JMenu editMenu = new JMenu(Bundle.getMessage("MenuEdit")); 513 JMenuItem preferencesItem = new JMenuItem(Bundle.getMessage("ThrottleMenuEditFrameProperties")); 514 editMenu.add(preferencesItem); 515 preferencesItem.addActionListener(e -> editPreferences()); 516 editMenuExportRoster = new JMenuItem(Bundle.getMessage("ThrottleMenuEditSaveCustoms")); 517 editMenu.add(editMenuExportRoster); 518 editMenuExportRoster.addActionListener(e -> getCurrentThrottleFrame().saveRosterChanges()); 519 editMenu.addSeparator(); 520 editMenu.add(new jmri.jmrit.throttle.ThrottlesPreferencesAction(Bundle.getMessage("MenuItemThrottlesPreferences"))); // now in tabbed preferences 521 522 this.setJMenuBar(new JMenuBar()); 523 this.getJMenuBar().add(fileMenu); 524 this.getJMenuBar().add(editMenu); 525 this.getJMenuBar().add(viewMenu); 526 527 if (powerMgr != null) { 528 JMenu powerMenu = new JMenu(Bundle.getMessage("ThrottleMenuPower")); 529 JMenuItem powerOn = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOn")); 530 powerMenu.add(powerOn); 531 powerOn.addActionListener(e -> { 532 try { 533 powerMgr.setPower(PowerManager.ON); 534 } catch (JmriException e1) { 535 log.error("Error when setting power: ", e1); 536 } 537 }); 538 539 JMenuItem powerOff = new JMenuItem(Bundle.getMessage("ThrottleMenuPowerOff")); 540 powerMenu.add(powerOff); 541 powerOff.addActionListener(e -> { 542 try { 543 powerMgr.setPower(PowerManager.OFF); 544 } catch (JmriException e1) { 545 log.error("Error when setting power: ", e1); 546 } 547 }); 548 549 this.getJMenuBar().add(powerMenu); 550 551 smallPowerMgmtButton = new SmallPowerManagerButton(); 552 this.getJMenuBar().add(smallPowerMgmtButton); 553 } 554 555 // add help selection 556 addHelpMenu("package.jmri.jmrit.throttle.ThrottleFrame", true); 557 } 558 559 private void editPreferences() { 560 ThrottleFramePropertyEditor editor = new ThrottleFramePropertyEditor(this); 561 editor.setVisible(true); 562 } 563 564 /** 565 * Handle my own destruction. 566 * <ol> 567 * <li> dispose of sub windows. 568 * <li> notify my manager of my demise. 569 * </ol> 570 * 571 */ 572 @Override 573 public void dispose() { 574 URIDrop.remove(throttleToolBar); 575 if ((throttleFrames != null) && (!throttleFrames.isEmpty())) { 576 for (ThrottleFrame tf: throttleFrames) { 577 tf.dispose(); 578 } 579 throttleFrames.clear(); 580 } 581 throttleFrames = null; 582 currentThrottleFrame = null; 583 for (PropertyChangeListener pcl : pcs.getPropertyChangeListeners()) { 584 pcs.removePropertyChangeListener(pcl); 585 } 586 for (MouseWheelListener mwl : getMouseWheelListeners()) { 587 removeMouseWheelListener(mwl); 588 } 589 getRootPane().getActionMap().clear(); 590 throttlesPanel.removeAll(); 591 removeAll(); 592 super.dispose(); 593 } 594 595 public JCheckBoxMenuItem getViewControlPanel() { 596 return viewControlPanel; 597 } 598 599 public JCheckBoxMenuItem getViewFunctionPanel() { 600 return viewFunctionPanel; 601 } 602 603 public JCheckBoxMenuItem getViewAddressPanel() { 604 return viewAddressPanel; 605 } 606 607 public JCheckBoxMenuItem getViewSpeedPanel() { 608 return viewSpeedPanel; 609 } 610 611 private void updateCurentThrottleFrame() { 612 currentThrottleFrame = null; 613 for (Component comp : throttlesPanel.getComponents()) { 614 if (comp instanceof ThrottleFrame && comp.isVisible()) { 615 currentThrottleFrame = (ThrottleFrame) comp; 616 } 617 } 618 } 619 620 public ThrottleFrame getCurrentThrottleFrame() { 621 return currentThrottleFrame; 622 } 623 624 public void setCurrentThrottleFrame(ThrottleFrame tf) { 625 if (getCurrentThrottleFrame() != null) { 626 log.debug("setCurrentThrottleFrame from {} to {}", getCurrentThrottleFrame().getAddressPanel().getCurrentAddress(), tf.getAddressPanel().getCurrentAddress()); 627 } 628 pcs.firePropertyChange("ThrottleFrameChanged", getCurrentThrottleFrame(), tf); 629 currentThrottleFrame = tf; 630 } 631 632 @Override 633 public void removeThrottleController(ThrottleControllerUI tf) { 634 if (getCurrentThrottleFrame() == tf) { 635 log.debug("Closing last created"); 636 } 637 throttlesPanel.remove((ThrottleFrame)tf); 638 throttleFrames.remove(tf); 639 throttlesLayout.invalidateLayout(throttlesPanel); 640 updateGUI(); 641 updateCurentThrottleFrame(); 642 pcs.firePropertyChange("ThrottleFrameRemoved", tf, getCurrentThrottleFrame()); 643 } 644 645 public void nextThrottleFrame() { 646 ThrottleFrame otf = getCurrentThrottleFrame(); 647 throttlesLayout.next(throttlesPanel); 648 updateCurentThrottleFrame(); 649 updateGUI(); 650 pcs.firePropertyChange("ThrottleFrameChanged", otf, getCurrentThrottleFrame()); 651 } 652 653 public void previousThrottleFrame() { 654 ThrottleFrame otf = getCurrentThrottleFrame(); 655 throttlesLayout.previous(throttlesPanel); 656 updateCurentThrottleFrame(); 657 updateGUI(); 658 pcs.firePropertyChange("ThrottleFrameChanged", otf, getCurrentThrottleFrame()); 659 } 660 661 public void nextRunningThrottleFrame() { 662 if (!throttleFrames.isEmpty()) { 663 ThrottleFrame cf = this.getCurrentThrottleFrame(); 664 ThrottleFrame nf = null; 665 boolean passed = false; 666 for (ThrottleFrame tf : throttleFrames) { 667 if (tf != cf) { 668 if ((tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) { 669 if (passed) { // if we passed the curent one, and found something then return it 670 nf = tf; 671 break; 672 } else if (nf == null) { 673 nf = tf; 674 } 675 } 676 } else { 677 passed = true; 678 } 679 } 680 if (nf != null) { 681 nf.toFront(); 682 updateCurentThrottleFrame(); 683 pcs.firePropertyChange("ThrottleFrameChanged", cf, nf); 684 } 685 } 686 } 687 688 public void previousRunningThrottleFrame() { 689 if (!throttleFrames.isEmpty()) { 690 ThrottleFrame cf = this.getCurrentThrottleFrame(); 691 ThrottleFrame nf = null; 692 for (ThrottleFrame tf : throttleFrames) { 693 if ((tf != cf) && (tf.getAddressPanel() != null) && (tf.getAddressPanel().getThrottle() != null) && (tf.getAddressPanel().getThrottle().getSpeedSetting() > 0)) { 694 nf = tf; 695 } 696 if ((tf == cf) && (nf != null)) { // return the last one found before the curent one 697 break; 698 } 699 } 700 if (nf != null) { 701 nf.toFront(); 702 updateCurentThrottleFrame(); 703 pcs.firePropertyChange("ThrottleFrameChanged", cf, nf); 704 } 705 } 706 } 707 708 private void removeAndDisposeCurentThrottleFrame() { 709 ThrottleFrame tf = getCurrentThrottleFrame(); 710 removeThrottleController(getCurrentThrottleFrame()); 711 tf.dispose(); 712 } 713 714 @Override 715 public void addThrottleControllerAt(ThrottleControllerUI tp, int idx) { 716 String txt = "ThrottleFrame-" + throttleFrameManager.generateUniqueFrameID(); 717 ((ThrottleFrame)tp).setTitle(txt); 718 if (idx>throttleFrames.size()) { 719 idx = throttleFrames.size(); 720 } 721 throttleFrames.add(idx,(ThrottleFrame)tp); 722 throttlesPanel.add((ThrottleFrame)tp,txt,idx); 723 ((ThrottleFrame)tp).setEditMode(isEditMode); // sync with window 724 updateGUI(); 725 pcs.firePropertyChange("ThrottleFrameAdded", null, this); 726 } 727 728 @Override 729 public ThrottleControllerUI newThrottleController() { 730 ThrottleFrame otf = getCurrentThrottleFrame(); 731 ThrottleFrame tf = new ThrottleFrame(this, throttleManager); 732 throttleFrames.add(tf); 733 String txt = "ThrottleFrame-" + throttleFrameManager.generateUniqueFrameID(); 734 tf.setTitle(txt); 735 throttlesPanel.add(tf, txt); 736 tf.setEditMode(isEditMode); // sync with window 737 installInputsListenerOnAllComponents(tf); 738 throttlesLayout.show(throttlesPanel, txt); 739 setCurrentThrottleFrame(tf); 740 updateGUI(); 741 pcs.firePropertyChange("ThrottleFrameNew", otf, getCurrentThrottleFrame()); 742 return getCurrentThrottleFrame(); 743 } 744 745 public void toFront(String throttleFrameTitle) { 746 ThrottleFrame otf = getCurrentThrottleFrame(); 747 throttlesLayout.show(throttlesPanel, throttleFrameTitle); 748 updateCurentThrottleFrame(); 749 setVisible(true); 750 requestFocus(); 751 toFront(); 752 pcs.firePropertyChange("ThrottleFrameChanged", otf, getCurrentThrottleFrame()); 753 } 754 755 public String getTitleTextType() { 756 return titleTextType; 757 } 758 759 public String getTitleText() { 760 return titleText; 761 } 762 763 public void setTitleText(String titleText) { 764 this.titleText = titleText; 765 } 766 767 public void setTitleTextType(String titleTextType) { 768 this.titleTextType = titleTextType; 769 } 770 771 public Element getXml() { 772 Element me = new Element("ThrottleWindow"); 773 if (connectionConfig != null) { 774 me.setAttribute("systemPrefix", connectionConfig.getAdapter().getSystemPrefix()); 775 } 776 me.setAttribute("title", titleText); 777 me.setAttribute("titleType", titleTextType); 778 me.setAttribute("isEditMode", String.valueOf(isEditMode)); 779 780 java.util.ArrayList<Element> children = new java.util.ArrayList<>(1); 781 children.add(WindowPreferences.getPreferences(this)); 782 if (!throttleFrames.isEmpty()) { 783 ThrottleFrame cf = this.getCurrentThrottleFrame(); 784 for (ThrottleFrame tf : throttleFrames) { 785 if ((InstanceManager.getDefault(ThrottlesPreferences.class).isUsingExThrottle()) && (InstanceManager.getDefault(ThrottlesPreferences.class).isSavingThrottleOnLayoutSave())) { 786 tf.toFront(); 787 tf.saveThrottle(); 788 } 789 Element tfe = tf.getXmlFile(); 790 if (tfe == null) { 791 tfe = tf.getXml(); 792 } 793 children.add(tfe); 794 } 795 if (cf != null) { 796 cf.toFront(); 797 } 798 } 799 800 // Save Jynstruments 801 if (throttleToolBar != null) { 802 Component[] cmps = throttleToolBar.getComponents(); 803 if (cmps != null) { 804 for (Component cmp : cmps) { 805 try { 806 if (cmp instanceof Jynstrument) { 807 Jynstrument jyn = (Jynstrument) cmp; 808 Element elt = new Element("Jynstrument"); 809 elt.setAttribute("JynstrumentFolder", FileUtil.getPortableFilename(jyn.getFolder())); 810 Element je = jyn.getXml(); 811 if (je != null) { 812 java.util.ArrayList<Element> jychildren = new java.util.ArrayList<>(1); 813 jychildren.add(je); 814 elt.setContent(jychildren); 815 } 816 children.add(elt); 817 } 818 819 } catch (Exception ex) { 820 log.debug("Got exception (no panic): ", ex); 821 } 822 } 823 } 824 } 825 me.setContent(children); 826 return me; 827 } 828 829 private void setXml(Element e) { 830 if (e.getAttribute("title") != null) { 831 setTitle(e.getAttribute("title").getValue()); 832 } 833 if (e.getAttribute("title") != null) { 834 setTitleText(e.getAttribute("title").getValue()); 835 } 836 if (e.getAttribute("titleType") != null) { 837 setTitleTextType(e.getAttribute("titleType").getValue()); 838 } 839 if (e.getAttribute("isEditMode") != null) { 840 isEditMode = Boolean.parseBoolean(e.getAttribute("isEditMode").getValue()); 841 } 842 843 Element window = e.getChild("window"); 844 if (window != null) { 845 WindowPreferences.setPreferences(this, window); 846 } 847 848 List<Element> tfes = e.getChildren("ThrottleFrame"); 849 if ((tfes != null) && (!tfes.isEmpty())) { 850 for (int i = 0; i < tfes.size(); i++) { 851 ThrottleFrame tf; 852 if (i == 0) { 853 tf = getCurrentThrottleFrame(); 854 } else { 855 tf = (ThrottleFrame) newThrottleController(); 856 } 857 tf.setXml(tfes.get(i)); 858 tf.setEditMode(isEditMode); 859 } 860 } 861 862 List<Element> jinsts = e.getChildren("Jynstrument"); 863 if ((jinsts != null) && (!jinsts.isEmpty())) { 864 jinsts.forEach((jinst) -> { 865 Jynstrument jyn = ynstrument(FileUtil.getExternalFilename(jinst.getAttributeValue("JynstrumentFolder"))); 866 if (jyn != null) { 867 jyn.setXml(jinst); 868 } 869 }); 870 } 871 872 updateGUI(); 873 } 874 875 @Override 876 public synchronized void addPropertyChangeListener(java.beans.PropertyChangeListener l) { 877 pcs.addPropertyChangeListener(l); 878 } 879 880 @Override 881 public synchronized void removePropertyChangeListener(java.beans.PropertyChangeListener l) { 882 pcs.removePropertyChangeListener(l); 883 } 884 885 private void installInputsListenerOnAllComponents(Container c) { 886 c.setFocusTraversalKeysEnabled(false); // make tab and shift tab available 887 if (! ( c instanceof JTextField)) { 888 c.setFocusable(false); 889 } 890 for (Component component : c.getComponents()) { 891 if (component instanceof Container) { 892 installInputsListenerOnAllComponents( (Container) component); 893 } else { 894 if (! ( component instanceof JTextField)) { 895 component.setFocusable(false); 896 } 897 } 898 } 899 } 900 901 public void applyPreferences() { 902 ThrottlesPreferences preferences = InstanceManager.getDefault(ThrottlesPreferences.class); 903 904 ComponentInputMap im = new ComponentInputMap(getRootPane()); 905 for (Object k : this.getRootPane().getActionMap().allKeys()) { 906 KeyStroke[] kss = preferences.getThrottlesKeyboardControls().getKeyStrokes((String)k); 907 if (kss !=null) { 908 for (KeyStroke keystroke : kss) { 909 if (keystroke != null) { 910 im.put(keystroke, k); 911 } 912 } 913 } 914 } 915 getRootPane().setInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW,im); 916 917 throttleToolBar.setVisible ( preferences.isUsingExThrottle() && preferences.isUsingToolBar() ); 918 919 if (smallPowerMgmtButton != null) { 920 smallPowerMgmtButton.setVisible( (!preferences.isUsingExThrottle()) || (!preferences.isUsingToolBar()) ); 921 } 922 923 for (ThrottleFrame tf : throttleFrames) { 924 tf.applyPreferences(); 925 } 926 } 927 928 private final static Logger log = LoggerFactory.getLogger(ThrottleWindow.class); 929}