001package jmri.jmrit.throttle.implementation; 002 003import java.awt.BorderLayout; 004import java.awt.Dimension; 005import java.awt.Frame; 006import java.awt.Window; 007import java.io.File; 008import java.io.FileNotFoundException; 009import java.io.IOException; 010import javax.swing.JFileChooser; 011import javax.swing.JLabel; 012import javax.swing.JPanel; 013 014import org.jdom2.Element; 015import org.jdom2.JDOMException; 016 017import jmri.DccLocoAddress; 018import jmri.DccThrottle; 019import jmri.InstanceManager; 020import jmri.ThrottleManager; 021import jmri.configurexml.LoadXmlConfigAction; 022import jmri.jmrit.roster.RosterEntry; 023import jmri.jmrit.roster.swing.RosterEntrySelectorPanel; 024import jmri.jmrit.throttle.ThrottleFrameManager; 025import jmri.jmrit.throttle.interfaces.AddressListener; 026import jmri.jmrit.throttle.interfaces.ThrottleControllerUI; 027import jmri.jmrit.throttle.interfaces.ThrottleControllersUIContainer; 028import jmri.jmrit.throttle.panels.ControlPanel; 029import jmri.util.FileUtil; 030import org.slf4j.Logger; 031import org.slf4j.LoggerFactory; 032 033/** 034 * A basic throttle panel, with control and function panels only 035 * No addressPanel, setAddress() or setConsistAddress() or setRosterEntry() has to be called 036 * to initiate a throttle request 037 * 038 * <hr> 039 * This file is part of JMRI. 040 * <p> 041 * JMRI is free software; you can redistribute it and/or modify it under the 042 * terms of version 2 of the GNU General Public License as published by the Free 043 * Software Foundation. See the "COPYING" file for a copy of this license. 044 * <p> 045 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 046 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 047 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 048 * 049 * @author Lionel Jeanson Copyright 2026 050 * 051 */ 052public class SimpleThrottlePanel extends JPanel implements ThrottleControllerUI { 053 054 private ThrottleControllersUIContainer myContainer; 055 private final ThrottleManager throttleManager; 056 private final ThrottleFrameManager throttleFrameManager = InstanceManager.getDefault(ThrottleFrameManager.class); 057 protected final ThrottleUICore throuic; 058 059 public SimpleThrottlePanel(ThrottleControllersUIContainer stw, ThrottleManager tm, boolean isShowingCtrlPanel, boolean isShowingFnPanel, boolean isShowingIconPanel) { 060 super(); 061 myContainer = stw; 062 throttleManager = tm; 063 throuic = new ThrottleUICore(throttleManager, this, false); 064 initGUI(isShowingCtrlPanel, isShowingFnPanel, isShowingIconPanel); 065 throuic.loadDefaultThrottle(); 066 throttleFrameManager.getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 067 } 068 069 private void initGUI(boolean isShowingCtrlPanel, boolean isShowingFnPanel, boolean isShowingIconPanel) { 070 setLayout(new BorderLayout()); 071 setOpaque(false); 072 if (isShowingCtrlPanel) { 073 add(throuic.getControlPanel(), BorderLayout.WEST); 074 } 075 if (isShowingFnPanel) { 076 add(throuic.getFunctionPanel(), BorderLayout.CENTER); 077 } 078 if (isShowingIconPanel) { 079 add(throuic.getLocoIconPanel(), BorderLayout.NORTH); 080 } 081 setPreferredSize(new Dimension(450,350)); 082 } 083 084 @Override 085 public void updateGUI() { 086 // nothing to do in this simple implementation as the GUI is directly updated by the ThrottleUICore 087 } 088 089 @Override 090 public void updateFrameTitle() { 091 String winTitle = Bundle.getMessage("ThrottleTitle"); 092 if ((throuic.getRosterEntry() != null) && (throuic.getRosterEntry().getId() != null)&& (throuic.getRosterEntry().getId().length() > 0)) { 093 winTitle = winTitle + " - " + throuic.getRosterEntry().getId(); 094 } 095 if ( getThrottle() != null) { 096 String addr = throuic.getAddressPanel().getCurrentAddress().toString(); 097 winTitle = winTitle + " - " + addr; 098 099 } 100 if (myContainer != null && myContainer instanceof Frame) { 101 ((Frame) myContainer).setTitle(winTitle); 102 } else { 103 log.debug("Unable to set simple throttle window title, myContainer is not an instance of Frame"); 104 } 105 } 106 107 /** 108 * Handle my own destruction. 109 * <ol> 110 * <li> dispose of sub windows. 111 * <li> notify my manager of my demise. 112 * </ol> 113 */ 114 public void dispose() { 115 log.debug("Disposing"); 116 throuic.dispose(); 117 removeAll(); 118 } 119 120 @Override 121 public ThrottleControllersUIContainer getThrottleControllersContainer() { 122 return myContainer; 123 } 124 125 @Override 126 public void setThrottleControllersContainer(ThrottleControllersUIContainer tw) { 127 myContainer = tw; 128 } 129 130 @Override 131 public void toFront() { 132 if (myContainer == null) { 133 return; 134 } 135 if (myContainer instanceof Window) { 136 ((Window) myContainer).toFront(); 137 } else { 138 log.warn("Unable to set simple throttle window to front, myContainer is not an instance of Window"); 139 } 140 } 141 142 @Override 143 public void setRosterEntry(RosterEntry re) { 144 throuic.setRosterEntry(re); 145 } 146 147 @Override 148 public RosterEntry getRosterEntry() { 149 return throuic.getRosterEntry(); 150 } 151 152 @Override 153 public void setAddress(DccLocoAddress la) { 154 throuic.setAddress(la); 155 } 156 157 @Override 158 public DccLocoAddress getAddress() { 159 return throuic.getAddress(); 160 } 161 162 @Override 163 public void setConsistAddress(DccLocoAddress la) { 164 throuic.setConsistAddress(la); 165 } 166 167 @Override 168 public boolean isUsingAddress(DccLocoAddress la) { 169 if ( getThrottle() != null && 170 ( la.equals( throuic.getAddressPanel().getCurrentAddress()) || la.equals( throuic.getAddressPanel().getConsistAddress()) ) ) { 171 return true; 172 } 173 return false; 174 } 175 176 @Override 177 public void eStop() { 178 throuic.eStop(); 179 } 180 181 @Override 182 public boolean isRunning() { 183 return ((getThrottle() != null) && (getThrottle().getSpeedSetting() > 0)); 184 } 185 186 @Override 187 public boolean isActive() { 188 return ( (getThrottle() != null) && ( (getThrottle().getSpeedSetting() > 0) || (throuic.hasActiveFunction()))); 189 } 190 191 @Override 192 public DccThrottle getThrottle() { 193 return throuic.getThrottle(); 194 } 195 196 @Override 197 public DccThrottle getFunctionThrottle() { 198 return throuic.getFunctionThrottle(); 199 } 200 201 @Override 202 public RosterEntry getFunctionRosterEntry() { 203 return throuic.getFunctionRosterEntry(); 204 } 205 206 @Override 207 public JLabel getLabel() { 208 return new JLabel(throuic.getLocoIconPanel().getDescription(), throuic.getLocoIconPanel().getIcon(), JLabel.CENTER); 209 } 210 211 @Override 212 public boolean isSpeedDisplayContinuous() { 213 return throuic.getControlPanel().getDisplaySlider() == ControlPanel.SLIDERDISPLAYCONTINUOUS; 214 } 215 216 public void saveRosterChanges() { 217 throuic.saveRosterChanges(); 218 } 219 220 @Override 221 public RosterEntrySelectorPanel getRosterEntrySelector() { 222 return throuic.getAddressPanel().getRosterEntrySelector(); 223 } 224 225 @Override 226 public void addAddressListener(AddressListener l) { 227 throuic.getAddressPanel().addAddressListener(l); 228 } 229 230 @Override 231 public void removeAddressListener(AddressListener l) { 232 throuic.getAddressPanel().removeAddressListener(l); 233 } 234 235 @Override 236 public void dispatchAddress() { 237 throuic.getAddressPanel().dispatchAddress(); 238 } 239 240 /** 241 * Sets the location of a throttle frame on the screen according to x and y 242 * coordinates 243 * 244 * @see java.awt.Component#setLocation(int, int) 245 */ 246 @Override 247 public void setLocation(int x, int y) { 248 if (myContainer instanceof Window) { 249 ((Window) myContainer).setLocation(x, y); 250 } 251 } 252 253 public Element getXmlFile() { 254 if (throuic.getLastUsedSaveFile() == null) { // || (getRosterEntry()==null)) 255 return null; 256 } 257 Element me = new Element("ThrottleFrame"); 258 me.setAttribute("ThrottleXMLFile", FileUtil.getPortableFilename(throuic.getLastUsedSaveFile())); 259 return me; 260 } 261 262 public void setXml(Element e) { 263 if (e == null) { 264 return; 265 } 266 267 String sfile = e.getAttributeValue("ThrottleXMLFile"); 268 if (sfile != null) { 269 loadThrottleFile(FileUtil.getExternalFilename(sfile)); 270 return; 271 } 272 273 } 274 275 @Override 276 public void loadThrottleFile(String sfile) { 277 if (sfile == null) { 278 JFileChooser fileChooser = jmri.jmrit.XmlFile.userFileChooser(Bundle.getMessage("PromptXmlFileTypes"), "xml"); 279 fileChooser.setCurrentDirectory(new File(ThrottleUICore.getDefaultThrottleFolder())); 280 fileChooser.setDialogType(JFileChooser.OPEN_DIALOG); 281 java.io.File file = LoadXmlConfigAction.getFile(fileChooser, this); 282 if (file == null) { 283 return ; 284 } 285 } 286 287 try { 288 Element conf = throuic.loadThrottle(sfile); 289 setXml(conf); 290 } catch (FileNotFoundException ex) { 291 // Don't show error dialog if file is not found 292 log.debug("Loading throttle exception: {}", ex.getMessage()); 293 log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile); 294 throuic.loadDefaultThrottle(); // revert to loading default one 295 } catch (NullPointerException | IOException | JDOMException ex) { 296 log.debug("Loading throttle exception: {}", ex.getMessage()); 297 log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile); 298 jmri.configurexml.ConfigXmlManager.creationErrorEncountered( 299 null, "parsing file " + sfile, 300 "Parse error", null, null, ex); 301 throuic.loadDefaultThrottle(); // revert to loading default one 302 } 303 } 304 305 private static final Logger log = LoggerFactory.getLogger(SimpleThrottlePanel.class); 306}