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 throttleFrameManager.getThrottlesListPanel().getTableModel().fireTableStructureChanged(); 066 } 067 068 private void initGUI(boolean isShowingCtrlPanel, boolean isShowingFnPanel, boolean isShowingIconPanel) { 069 setLayout(new BorderLayout()); 070 setOpaque(false); 071 if (isShowingCtrlPanel) { 072 add(throuic.getControlPanel(), BorderLayout.WEST); 073 } 074 if (isShowingFnPanel) { 075 add(throuic.getFunctionPanel(), BorderLayout.CENTER); 076 } 077 if (isShowingIconPanel) { 078 add(throuic.getLocoIconPanel(), BorderLayout.NORTH); 079 } 080 setPreferredSize(new Dimension(450,350)); 081 } 082 083 @Override 084 public void updateGUI() { 085 // nothing to do in this simple implementation as the GUI is directly updated by the ThrottleUICore 086 } 087 088 @Override 089 public void updateFrameTitle() { 090 String winTitle = Bundle.getMessage("ThrottleTitle"); 091 if ((throuic.getRosterEntry() != null) && (throuic.getRosterEntry().getId() != null)&& (throuic.getRosterEntry().getId().length() > 0)) { 092 winTitle = winTitle + " - " + throuic.getRosterEntry().getId(); 093 } 094 if ( getThrottle() != null) { 095 String addr = throuic.getAddressPanel().getCurrentAddress().toString(); 096 winTitle = winTitle + " - " + addr; 097 098 } 099 if (myContainer != null && myContainer instanceof Frame) { 100 ((Frame) myContainer).setTitle(winTitle); 101 } else { 102 log.debug("Unable to set simple throttle window title, myContainer is not an instance of Frame"); 103 } 104 } 105 106 /** 107 * Handle my own destruction. 108 * <ol> 109 * <li> dispose of sub windows. 110 * <li> notify my manager of my demise. 111 * </ol> 112 */ 113 public void dispose() { 114 log.debug("Disposing"); 115 throuic.dispose(); 116 removeAll(); 117 } 118 119 @Override 120 public ThrottleControllersUIContainer getThrottleControllersContainer() { 121 return myContainer; 122 } 123 124 @Override 125 public void setThrottleControllersContainer(ThrottleControllersUIContainer tw) { 126 myContainer = tw; 127 } 128 129 @Override 130 public void toFront() { 131 if (myContainer == null) { 132 return; 133 } 134 if (myContainer instanceof Window) { 135 ((Window) myContainer).toFront(); 136 } else { 137 log.warn("Unable to set simple throttle window to front, myContainer is not an instance of Window"); 138 } 139 } 140 141 @Override 142 public void setRosterEntry(RosterEntry re) { 143 throuic.setRosterEntry(re); 144 } 145 146 @Override 147 public RosterEntry getRosterEntry() { 148 return throuic.getRosterEntry(); 149 } 150 151 @Override 152 public void setAddress(DccLocoAddress la) { 153 throuic.setAddress(la); 154 } 155 156 @Override 157 public DccLocoAddress getAddress() { 158 return throuic.getAddress(); 159 } 160 161 @Override 162 public void setConsistAddress(DccLocoAddress la) { 163 throuic.setConsistAddress(la); 164 } 165 166 @Override 167 public boolean isUsingAddress(DccLocoAddress la) { 168 if ( getThrottle() != null && 169 ( la.equals( throuic.getAddressPanel().getCurrentAddress()) || la.equals( throuic.getAddressPanel().getConsistAddress()) ) ) { 170 return true; 171 } 172 return false; 173 } 174 175 @Override 176 public void eStop() { 177 throuic.eStop(); 178 } 179 180 @Override 181 public boolean isRunning() { 182 return ((getThrottle() != null) && (getThrottle().getSpeedSetting() > 0)); 183 } 184 185 @Override 186 public boolean isActive() { 187 return ( (getThrottle() != null) && ( (getThrottle().getSpeedSetting() > 0) || (throuic.hasActiveFunction()))); 188 } 189 190 @Override 191 public DccThrottle getThrottle() { 192 return throuic.getThrottle(); 193 } 194 195 @Override 196 public DccThrottle getFunctionThrottle() { 197 return throuic.getFunctionThrottle(); 198 } 199 200 @Override 201 public RosterEntry getFunctionRosterEntry() { 202 return throuic.getFunctionRosterEntry(); 203 } 204 205 @Override 206 public JLabel getLabel() { 207 return new JLabel(throuic.getLocoIconPanel().getDescription(), throuic.getLocoIconPanel().getIcon(), JLabel.CENTER); 208 } 209 210 @Override 211 public boolean isSpeedDisplayContinuous() { 212 return throuic.getControlPanel().getDisplaySlider() == ControlPanel.SLIDERDISPLAYCONTINUOUS; 213 } 214 215 public void saveRosterChanges() { 216 throuic.saveRosterChanges(); 217 } 218 219 @Override 220 public RosterEntrySelectorPanel getRosterEntrySelector() { 221 return throuic.getAddressPanel().getRosterEntrySelector(); 222 } 223 224 @Override 225 public void addAddressListener(AddressListener l) { 226 throuic.getAddressPanel().addAddressListener(l); 227 } 228 229 @Override 230 public void removeAddressListener(AddressListener l) { 231 throuic.getAddressPanel().removeAddressListener(l); 232 } 233 234 @Override 235 public void dispatchAddress() { 236 throuic.getAddressPanel().dispatchAddress(); 237 } 238 239 /** 240 * Sets the location of a throttle frame on the screen according to x and y 241 * coordinates 242 * 243 * @see java.awt.Component#setLocation(int, int) 244 */ 245 @Override 246 public void setLocation(int x, int y) { 247 if (myContainer instanceof Window) { 248 ((Window) myContainer).setLocation(x, y); 249 } 250 } 251 252 public Element getXmlFile() { 253 if (throuic.getLastUsedSaveFile() == null) { // || (getRosterEntry()==null)) 254 return null; 255 } 256 Element me = new Element("ThrottleFrame"); 257 me.setAttribute("ThrottleXMLFile", FileUtil.getPortableFilename(throuic.getLastUsedSaveFile())); 258 return me; 259 } 260 261 public void setXml(Element e) { 262 if (e == null) { 263 return; 264 } 265 266 String sfile = e.getAttributeValue("ThrottleXMLFile"); 267 if (sfile != null) { 268 loadThrottleFile(FileUtil.getExternalFilename(sfile)); 269 return; 270 } 271 272 } 273 274 @Override 275 public void loadThrottleFile(String sfile) { 276 if (sfile == null) { 277 JFileChooser fileChooser = jmri.jmrit.XmlFile.userFileChooser(Bundle.getMessage("PromptXmlFileTypes"), "xml"); 278 fileChooser.setCurrentDirectory(new File(ThrottleUICore.getDefaultThrottleFolder())); 279 fileChooser.setDialogType(JFileChooser.OPEN_DIALOG); 280 java.io.File file = LoadXmlConfigAction.getFile(fileChooser, this); 281 if (file == null) { 282 return ; 283 } 284 } 285 286 try { 287 Element conf = throuic.loadThrottle(sfile); 288 setXml(conf); 289 } catch (FileNotFoundException ex) { 290 // Don't show error dialog if file is not found 291 log.debug("Loading throttle exception: {}", ex.getMessage()); 292 log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile); 293 } catch (NullPointerException | IOException | JDOMException ex) { 294 log.debug("Loading throttle exception: {}", ex.getMessage()); 295 log.debug("Tried loading throttle file \"{}\" , reverting to default, if any", sfile); 296 jmri.configurexml.ConfigXmlManager.creationErrorEncountered( 297 null, "parsing file " + sfile, 298 "Parse error", null, null, ex); 299 } 300 } 301 302 private static final Logger log = LoggerFactory.getLogger(SimpleThrottlePanel.class); 303}