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}